├── README.md ├── gameninja.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── benzi.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── benzi.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── gameninja.xcscheme │ └── xcschememanagement.plist ├── gameninja ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── Components.swift ├── Entity.swift ├── EntityFactory.swift ├── Extensions.swift ├── GameOverScene.swift ├── GameScene.sks ├── GameScene.swift ├── GameSpriteNode.swift ├── GameViewController.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── fire.imageset │ │ ├── Contents.json │ │ └── fire.png │ ├── land.imageset │ │ ├── Contents.json │ │ └── land.png │ ├── monster.imageset │ │ ├── Contents.json │ │ └── monster.png │ ├── ninja.imageset │ │ ├── Contents.json │ │ └── ninja.png │ └── robot.imageset │ │ ├── Contents.json │ │ └── brown.png ├── Info.plist ├── Systems.swift ├── background-music-aac.caf └── pew-pew-lei.caf ├── gameninjaTests ├── Info.plist └── gameninjaTests.swift └── screen.png /README.md: -------------------------------------------------------------------------------- 1 | 2 | # gameninja 3 | 4 | ![](https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/master/screen.png) 5 | 6 | 7 | A simple game (being) written in Swift using SpriteKit. 8 | 9 | Initially based on the beginner tutorial [from raywenderlich.com](http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners). Work in progress modification to an entity system model, as outlined [here](http://www.raywenderlich.com/24878/introduction-to-component-based-architecture-in-games). 10 | 11 | Sprites from the [Favourite Monster icon set](https://www.iconfinder.com/iconsets/Favorite_monsters) 12 | -------------------------------------------------------------------------------- /gameninja.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B908F3341941DE8200694E09 /* pew-pew-lei.caf in Resources */ = {isa = PBXBuildFile; fileRef = B908F3331941DE8100694E09 /* pew-pew-lei.caf */; }; 11 | B908F3361941E1F200694E09 /* background-music-aac.caf in Resources */ = {isa = PBXBuildFile; fileRef = B908F3351941E1F200694E09 /* background-music-aac.caf */; }; 12 | B962FEA71952DD7700FBA15E /* Entity.swift in Sources */ = {isa = PBXBuildFile; fileRef = B962FEA61952DD7700FBA15E /* Entity.swift */; }; 13 | B98CF6C51953474500193884 /* Systems.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CF6C41953474500193884 /* Systems.swift */; }; 14 | B98CF6C71953478400193884 /* EntityFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CF6C61953478400193884 /* EntityFactory.swift */; }; 15 | B98CF6C919535A6D00193884 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CF6C819535A6C00193884 /* Extensions.swift */; }; 16 | B98CF6CC19536EBD00193884 /* GameSpriteNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CF6CB19536EBD00193884 /* GameSpriteNode.swift */; }; 17 | B99B927E19533A8300EE9F39 /* Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99B927D19533A8300EE9F39 /* Components.swift */; }; 18 | B99DF79E1940761900745CE9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99DF79D1940761900745CE9 /* AppDelegate.swift */; }; 19 | B99DF7A01940761900745CE9 /* GameScene.sks in Resources */ = {isa = PBXBuildFile; fileRef = B99DF79F1940761900745CE9 /* GameScene.sks */; }; 20 | B99DF7A21940761900745CE9 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99DF7A11940761900745CE9 /* GameScene.swift */; }; 21 | B99DF7A41940761900745CE9 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99DF7A31940761900745CE9 /* GameViewController.swift */; }; 22 | B99DF7A71940761900745CE9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B99DF7A51940761900745CE9 /* Main.storyboard */; }; 23 | B99DF7A91940761900745CE9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B99DF7A81940761900745CE9 /* Images.xcassets */; }; 24 | B99DF7B51940761900745CE9 /* gameninjaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99DF7B41940761900745CE9 /* gameninjaTests.swift */; }; 25 | B9BA04141940E0DE0002A594 /* GameOverScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9BA04131940E0DD0002A594 /* GameOverScene.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | B99DF7AF1940761900745CE9 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = B99DF7901940761800745CE9 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = B99DF7971940761900745CE9; 34 | remoteInfo = gameninja; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | B908F3331941DE8100694E09 /* pew-pew-lei.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "pew-pew-lei.caf"; sourceTree = ""; }; 40 | B908F3351941E1F200694E09 /* background-music-aac.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "background-music-aac.caf"; sourceTree = ""; }; 41 | B962FEA61952DD7700FBA15E /* Entity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Entity.swift; sourceTree = ""; }; 42 | B98CF6C41953474500193884 /* Systems.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Systems.swift; sourceTree = ""; }; 43 | B98CF6C61953478400193884 /* EntityFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityFactory.swift; sourceTree = ""; }; 44 | B98CF6C819535A6C00193884 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 45 | B98CF6CB19536EBD00193884 /* GameSpriteNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameSpriteNode.swift; sourceTree = ""; }; 46 | B99B927D19533A8300EE9F39 /* Components.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Components.swift; sourceTree = ""; }; 47 | B99DF7981940761900745CE9 /* gameninja.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = gameninja.app; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | B99DF79C1940761900745CE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | B99DF79D1940761900745CE9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50 | B99DF79F1940761900745CE9 /* GameScene.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = GameScene.sks; sourceTree = ""; }; 51 | B99DF7A11940761900745CE9 /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScene.swift; sourceTree = ""; }; 52 | B99DF7A31940761900745CE9 /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 53 | B99DF7A61940761900745CE9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 54 | B99DF7A81940761900745CE9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 55 | B99DF7AE1940761900745CE9 /* gameninjaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = gameninjaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | B99DF7B31940761900745CE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | B99DF7B41940761900745CE9 /* gameninjaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = gameninjaTests.swift; sourceTree = ""; }; 58 | B9BA04131940E0DD0002A594 /* GameOverScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameOverScene.swift; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | B99DF7951940761900745CE9 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | B99DF7AB1940761900745CE9 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXFrameworksBuildPhase section */ 77 | 78 | /* Begin PBXGroup section */ 79 | B962FEA51952DD5B00FBA15E /* EntityModel */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | B962FEA61952DD7700FBA15E /* Entity.swift */, 83 | B98CF6C61953478400193884 /* EntityFactory.swift */, 84 | B99B927D19533A8300EE9F39 /* Components.swift */, 85 | B98CF6C41953474500193884 /* Systems.swift */, 86 | ); 87 | name = EntityModel; 88 | sourceTree = ""; 89 | }; 90 | B98CF6CA19536E7A00193884 /* GameNodes */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | B98CF6CB19536EBD00193884 /* GameSpriteNode.swift */, 94 | ); 95 | name = GameNodes; 96 | sourceTree = ""; 97 | }; 98 | B99DF78F1940761800745CE9 = { 99 | isa = PBXGroup; 100 | children = ( 101 | B99DF79A1940761900745CE9 /* gameninja */, 102 | B99DF7B11940761900745CE9 /* gameninjaTests */, 103 | B99DF7991940761900745CE9 /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | B99DF7991940761900745CE9 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | B99DF7981940761900745CE9 /* gameninja.app */, 111 | B99DF7AE1940761900745CE9 /* gameninjaTests.xctest */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | B99DF79A1940761900745CE9 /* gameninja */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | B98CF6CA19536E7A00193884 /* GameNodes */, 120 | B962FEA51952DD5B00FBA15E /* EntityModel */, 121 | B99DF79D1940761900745CE9 /* AppDelegate.swift */, 122 | B99DF7A11940761900745CE9 /* GameScene.swift */, 123 | B98CF6C819535A6C00193884 /* Extensions.swift */, 124 | B9BA04131940E0DD0002A594 /* GameOverScene.swift */, 125 | B99DF7A31940761900745CE9 /* GameViewController.swift */, 126 | B99DF7A51940761900745CE9 /* Main.storyboard */, 127 | B99DF79F1940761900745CE9 /* GameScene.sks */, 128 | B99DF7A81940761900745CE9 /* Images.xcassets */, 129 | B99DF79B1940761900745CE9 /* Supporting Files */, 130 | ); 131 | path = gameninja; 132 | sourceTree = ""; 133 | }; 134 | B99DF79B1940761900745CE9 /* Supporting Files */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | B908F3351941E1F200694E09 /* background-music-aac.caf */, 138 | B908F3331941DE8100694E09 /* pew-pew-lei.caf */, 139 | B99DF79C1940761900745CE9 /* Info.plist */, 140 | ); 141 | name = "Supporting Files"; 142 | sourceTree = ""; 143 | }; 144 | B99DF7B11940761900745CE9 /* gameninjaTests */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | B99DF7B41940761900745CE9 /* gameninjaTests.swift */, 148 | B99DF7B21940761900745CE9 /* Supporting Files */, 149 | ); 150 | path = gameninjaTests; 151 | sourceTree = ""; 152 | }; 153 | B99DF7B21940761900745CE9 /* Supporting Files */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | B99DF7B31940761900745CE9 /* Info.plist */, 157 | ); 158 | name = "Supporting Files"; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | B99DF7971940761900745CE9 /* gameninja */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = B99DF7B81940761900745CE9 /* Build configuration list for PBXNativeTarget "gameninja" */; 167 | buildPhases = ( 168 | B99DF7941940761900745CE9 /* Sources */, 169 | B99DF7951940761900745CE9 /* Frameworks */, 170 | B99DF7961940761900745CE9 /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = gameninja; 177 | productName = gameninja; 178 | productReference = B99DF7981940761900745CE9 /* gameninja.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | B99DF7AD1940761900745CE9 /* gameninjaTests */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = B99DF7BB1940761900745CE9 /* Build configuration list for PBXNativeTarget "gameninjaTests" */; 184 | buildPhases = ( 185 | B99DF7AA1940761900745CE9 /* Sources */, 186 | B99DF7AB1940761900745CE9 /* Frameworks */, 187 | B99DF7AC1940761900745CE9 /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | B99DF7B01940761900745CE9 /* PBXTargetDependency */, 193 | ); 194 | name = gameninjaTests; 195 | productName = gameninjaTests; 196 | productReference = B99DF7AE1940761900745CE9 /* gameninjaTests.xctest */; 197 | productType = "com.apple.product-type.bundle.unit-test"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | B99DF7901940761800745CE9 /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | LastUpgradeCheck = 0600; 206 | ORGANIZATIONNAME = "Benzi Ahamed"; 207 | TargetAttributes = { 208 | B99DF7971940761900745CE9 = { 209 | CreatedOnToolsVersion = 6.0; 210 | }; 211 | B99DF7AD1940761900745CE9 = { 212 | CreatedOnToolsVersion = 6.0; 213 | TestTargetID = B99DF7971940761900745CE9; 214 | }; 215 | }; 216 | }; 217 | buildConfigurationList = B99DF7931940761900745CE9 /* Build configuration list for PBXProject "gameninja" */; 218 | compatibilityVersion = "Xcode 3.2"; 219 | developmentRegion = English; 220 | hasScannedForEncodings = 0; 221 | knownRegions = ( 222 | en, 223 | Base, 224 | ); 225 | mainGroup = B99DF78F1940761800745CE9; 226 | productRefGroup = B99DF7991940761900745CE9 /* Products */; 227 | projectDirPath = ""; 228 | projectRoot = ""; 229 | targets = ( 230 | B99DF7971940761900745CE9 /* gameninja */, 231 | B99DF7AD1940761900745CE9 /* gameninjaTests */, 232 | ); 233 | }; 234 | /* End PBXProject section */ 235 | 236 | /* Begin PBXResourcesBuildPhase section */ 237 | B99DF7961940761900745CE9 /* Resources */ = { 238 | isa = PBXResourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | B908F3361941E1F200694E09 /* background-music-aac.caf in Resources */, 242 | B99DF7A91940761900745CE9 /* Images.xcassets in Resources */, 243 | B99DF7A01940761900745CE9 /* GameScene.sks in Resources */, 244 | B99DF7A71940761900745CE9 /* Main.storyboard in Resources */, 245 | B908F3341941DE8200694E09 /* pew-pew-lei.caf in Resources */, 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | B99DF7AC1940761900745CE9 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXResourcesBuildPhase section */ 257 | 258 | /* Begin PBXSourcesBuildPhase section */ 259 | B99DF7941940761900745CE9 /* Sources */ = { 260 | isa = PBXSourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | B99DF7A21940761900745CE9 /* GameScene.swift in Sources */, 264 | B98CF6C919535A6D00193884 /* Extensions.swift in Sources */, 265 | B98CF6CC19536EBD00193884 /* GameSpriteNode.swift in Sources */, 266 | B962FEA71952DD7700FBA15E /* Entity.swift in Sources */, 267 | B99DF7A41940761900745CE9 /* GameViewController.swift in Sources */, 268 | B98CF6C71953478400193884 /* EntityFactory.swift in Sources */, 269 | B9BA04141940E0DE0002A594 /* GameOverScene.swift in Sources */, 270 | B99DF79E1940761900745CE9 /* AppDelegate.swift in Sources */, 271 | B99B927E19533A8300EE9F39 /* Components.swift in Sources */, 272 | B98CF6C51953474500193884 /* Systems.swift in Sources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | B99DF7AA1940761900745CE9 /* Sources */ = { 277 | isa = PBXSourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | B99DF7B51940761900745CE9 /* gameninjaTests.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXSourcesBuildPhase section */ 285 | 286 | /* Begin PBXTargetDependency section */ 287 | B99DF7B01940761900745CE9 /* PBXTargetDependency */ = { 288 | isa = PBXTargetDependency; 289 | target = B99DF7971940761900745CE9 /* gameninja */; 290 | targetProxy = B99DF7AF1940761900745CE9 /* PBXContainerItemProxy */; 291 | }; 292 | /* End PBXTargetDependency section */ 293 | 294 | /* Begin PBXVariantGroup section */ 295 | B99DF7A51940761900745CE9 /* Main.storyboard */ = { 296 | isa = PBXVariantGroup; 297 | children = ( 298 | B99DF7A61940761900745CE9 /* Base */, 299 | ); 300 | name = Main.storyboard; 301 | sourceTree = ""; 302 | }; 303 | /* End PBXVariantGroup section */ 304 | 305 | /* Begin XCBuildConfiguration section */ 306 | B99DF7B61940761900745CE9 /* Debug */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BOOL_CONVERSION = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = NO; 325 | ENABLE_STRICT_OBJC_MSGSEND = YES; 326 | GCC_C_LANGUAGE_STANDARD = gnu99; 327 | GCC_DYNAMIC_NO_PIC = NO; 328 | GCC_OPTIMIZATION_LEVEL = 0; 329 | GCC_PREPROCESSOR_DEFINITIONS = ( 330 | "DEBUG=1", 331 | "$(inherited)", 332 | ); 333 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 341 | METAL_ENABLE_DEBUG_INFO = YES; 342 | ONLY_ACTIVE_ARCH = YES; 343 | SDKROOT = iphoneos; 344 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 345 | TARGETED_DEVICE_FAMILY = "1,2"; 346 | }; 347 | name = Debug; 348 | }; 349 | B99DF7B71940761900745CE9 /* Release */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | ALWAYS_SEARCH_USER_PATHS = NO; 353 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 354 | CLANG_CXX_LIBRARY = "libc++"; 355 | CLANG_ENABLE_MODULES = YES; 356 | CLANG_ENABLE_OBJC_ARC = YES; 357 | CLANG_WARN_BOOL_CONVERSION = YES; 358 | CLANG_WARN_CONSTANT_CONVERSION = YES; 359 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 360 | CLANG_WARN_EMPTY_BODY = YES; 361 | CLANG_WARN_ENUM_CONVERSION = YES; 362 | CLANG_WARN_INT_CONVERSION = YES; 363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 364 | CLANG_WARN_UNREACHABLE_CODE = YES; 365 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 366 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 367 | COPY_PHASE_STRIP = YES; 368 | ENABLE_NS_ASSERTIONS = NO; 369 | ENABLE_STRICT_OBJC_MSGSEND = YES; 370 | GCC_C_LANGUAGE_STANDARD = gnu99; 371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 373 | GCC_WARN_UNDECLARED_SELECTOR = YES; 374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 375 | GCC_WARN_UNUSED_FUNCTION = YES; 376 | GCC_WARN_UNUSED_VARIABLE = YES; 377 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 378 | METAL_ENABLE_DEBUG_INFO = NO; 379 | SDKROOT = iphoneos; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | VALIDATE_PRODUCT = YES; 382 | }; 383 | name = Release; 384 | }; 385 | B99DF7B91940761900745CE9 /* Debug */ = { 386 | isa = XCBuildConfiguration; 387 | buildSettings = { 388 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 389 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 390 | INFOPLIST_FILE = gameninja/Info.plist; 391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | }; 394 | name = Debug; 395 | }; 396 | B99DF7BA1940761900745CE9 /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 400 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 401 | INFOPLIST_FILE = gameninja/Info.plist; 402 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | }; 405 | name = Release; 406 | }; 407 | B99DF7BC1940761900745CE9 /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/gameninja.app/gameninja"; 411 | FRAMEWORK_SEARCH_PATHS = ( 412 | "$(SDKROOT)/Developer/Library/Frameworks", 413 | "$(inherited)", 414 | ); 415 | GCC_PREPROCESSOR_DEFINITIONS = ( 416 | "DEBUG=1", 417 | "$(inherited)", 418 | ); 419 | INFOPLIST_FILE = gameninjaTests/Info.plist; 420 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 421 | METAL_ENABLE_DEBUG_INFO = YES; 422 | PRODUCT_NAME = "$(TARGET_NAME)"; 423 | TEST_HOST = "$(BUNDLE_LOADER)"; 424 | }; 425 | name = Debug; 426 | }; 427 | B99DF7BD1940761900745CE9 /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/gameninja.app/gameninja"; 431 | FRAMEWORK_SEARCH_PATHS = ( 432 | "$(SDKROOT)/Developer/Library/Frameworks", 433 | "$(inherited)", 434 | ); 435 | INFOPLIST_FILE = gameninjaTests/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 437 | METAL_ENABLE_DEBUG_INFO = NO; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | TEST_HOST = "$(BUNDLE_LOADER)"; 440 | }; 441 | name = Release; 442 | }; 443 | /* End XCBuildConfiguration section */ 444 | 445 | /* Begin XCConfigurationList section */ 446 | B99DF7931940761900745CE9 /* Build configuration list for PBXProject "gameninja" */ = { 447 | isa = XCConfigurationList; 448 | buildConfigurations = ( 449 | B99DF7B61940761900745CE9 /* Debug */, 450 | B99DF7B71940761900745CE9 /* Release */, 451 | ); 452 | defaultConfigurationIsVisible = 0; 453 | defaultConfigurationName = Release; 454 | }; 455 | B99DF7B81940761900745CE9 /* Build configuration list for PBXNativeTarget "gameninja" */ = { 456 | isa = XCConfigurationList; 457 | buildConfigurations = ( 458 | B99DF7B91940761900745CE9 /* Debug */, 459 | B99DF7BA1940761900745CE9 /* Release */, 460 | ); 461 | defaultConfigurationIsVisible = 0; 462 | defaultConfigurationName = Release; 463 | }; 464 | B99DF7BB1940761900745CE9 /* Build configuration list for PBXNativeTarget "gameninjaTests" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | B99DF7BC1940761900745CE9 /* Debug */, 468 | B99DF7BD1940761900745CE9 /* Release */, 469 | ); 470 | defaultConfigurationIsVisible = 0; 471 | defaultConfigurationName = Release; 472 | }; 473 | /* End XCConfigurationList section */ 474 | }; 475 | rootObject = B99DF7901940761800745CE9 /* Project object */; 476 | } 477 | -------------------------------------------------------------------------------- /gameninja.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gameninja.xcodeproj/project.xcworkspace/xcuserdata/benzi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja.xcodeproj/project.xcworkspace/xcuserdata/benzi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /gameninja.xcodeproj/xcuserdata/benzi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 70 | 72 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /gameninja.xcodeproj/xcuserdata/benzi.xcuserdatad/xcschemes/gameninja.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /gameninja.xcodeproj/xcuserdata/benzi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | gameninja.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B99DF7971940761900745CE9 16 | 17 | primary 18 | 19 | 20 | B99DF7AD1940761900745CE9 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /gameninja/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 05/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /gameninja/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /gameninja/Components.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Categories.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 19/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | 13 | // MARK: Components ------------------------------------------ 14 | 15 | enum ComponentType { 16 | case Render 17 | case Health 18 | case Motion 19 | case Collision 20 | case SpecialDeath 21 | case HealthDecay 22 | case HealthBar 23 | } 24 | 25 | class Component { 26 | var type:ComponentType 27 | init(type:ComponentType) { self.type = type } 28 | } 29 | 30 | 31 | class RenderComponent : Component { 32 | var node:SKSpriteNode 33 | 34 | init(node:SKSpriteNode){ 35 | self.node = node 36 | super.init(type: ComponentType.Render) 37 | } 38 | } 39 | 40 | class MotionComponent : Component { 41 | 42 | var destination:CGPoint 43 | var speed:Double 44 | var inMotion = false 45 | 46 | init(destination:CGPoint, speed:Double) { 47 | self.destination = destination 48 | self.speed = speed 49 | super.init(type: ComponentType.Motion) 50 | } 51 | 52 | } 53 | 54 | class HealthComponent : Component { 55 | 56 | var isAlive:Bool 57 | var currentHealth:Double 58 | var maxHealth:Double 59 | 60 | var percent:Double { return currentHealth/maxHealth } 61 | 62 | init(currentHealth:Double, maxHealth:Double){ 63 | self.currentHealth = currentHealth 64 | self.maxHealth = maxHealth 65 | self.isAlive = true 66 | super.init(type: ComponentType.Health) 67 | } 68 | } 69 | 70 | 71 | class CollisionRule { 72 | var hitCategory:UInt32 73 | var damageGiven:Double 74 | var damageSustained:Double 75 | 76 | init(hitCategory:UInt32, damageGiven:Double, damageSustained:Double){ 77 | self.hitCategory = hitCategory 78 | self.damageGiven = damageGiven 79 | self.damageSustained = damageSustained 80 | } 81 | 82 | func appliesTo(category:UInt32) -> Bool { 83 | return (hitCategory & category) > 0 84 | } 85 | } 86 | 87 | class CollisionComponent : Component { 88 | 89 | var rules:CollisionRule[] 90 | 91 | init(collisionRules:CollisionRule[]) { 92 | self.rules = collisionRules 93 | super.init(type: ComponentType.Collision) 94 | } 95 | } 96 | 97 | class DramaticDeath : Component { 98 | 99 | var action:()->() 100 | 101 | init(action:()->()) { 102 | self.action = action 103 | super.init(type: ComponentType.SpecialDeath) 104 | } 105 | } 106 | 107 | class HealthDecayComponent : Component { 108 | var factor:Double 109 | 110 | init(factor:Double){ 111 | self.factor = factor 112 | super.init(type: ComponentType.HealthDecay) 113 | } 114 | } 115 | 116 | class RenderHealthBarComponent : Component { 117 | init() { 118 | super.init(type: ComponentType.HealthBar) 119 | } 120 | } 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /gameninja/Entity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Entity.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 19/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | 13 | // MARK: Entity ------------------------------------------ 14 | 15 | class Entity { 16 | var id:UInt 17 | 18 | init(id:UInt){ 19 | self.id = id 20 | } 21 | } 22 | 23 | 24 | 25 | 26 | // MARK: EntityManager ------------------------------------------ 27 | 28 | class EntityManager { 29 | 30 | struct EntityId{ 31 | static var lowestIdCreated:UInt = 0 32 | } 33 | 34 | var entites:NSMutableArray 35 | var componentsByType:Dictionary 36 | 37 | init(){ 38 | entites = NSMutableArray() 39 | componentsByType = Dictionary() 40 | } 41 | 42 | func createEntityId() -> UInt { 43 | if EntityId.lowestIdCreated < UInt.max { 44 | return EntityId.lowestIdCreated++ 45 | } else { 46 | for i in 1..UInt.max { 47 | if !self.entites.containsObject(i) { 48 | return i 49 | } 50 | } 51 | } 52 | return 0 53 | } 54 | 55 | func createEntity() -> Entity { 56 | let e = Entity(id: createEntityId()) 57 | entites.addObject(e.id) 58 | return e 59 | } 60 | 61 | func removeEntity(e:Entity) { 62 | for components in componentsByType.values { 63 | if components[e.id] != nil { 64 | components.removeObjectForKey(e.id) 65 | } 66 | } 67 | entites.removeObject(e.id) 68 | } 69 | 70 | func addComponent(e:Entity, c:Component){ 71 | if componentsByType[c.type] == nil { 72 | componentsByType[c.type] = NSMutableDictionary() 73 | } 74 | let componentMap = componentsByType[c.type]! 75 | componentMap[e.id] = c 76 | } 77 | 78 | func getComponent(e:Entity, type:ComponentType) -> Component? { 79 | if let componentSet = componentsByType[type] as? NSMutableDictionary { 80 | return componentSet[e.id] as? Component 81 | } 82 | return nil 83 | } 84 | 85 | func getEntitiesWithComponent(type:ComponentType) -> Entity[] { 86 | var matches = Entity[]() 87 | if let components = componentsByType[type] as? NSMutableDictionary { 88 | for eid : AnyObject in components.allKeys { 89 | matches.append(Entity(id: eid as UInt)) 90 | } 91 | } 92 | return matches 93 | } 94 | 95 | } 96 | 97 | 98 | -------------------------------------------------------------------------------- /gameninja/EntityFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EntityFactory.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 19/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | 13 | // MARK: EntityFactory ------------------------------------------ 14 | 15 | class EntityFactory { 16 | var entityManager:EntityManager 17 | var scene:GameScene 18 | 19 | init(entityManager:EntityManager, scene:GameScene){ 20 | self.entityManager = entityManager 21 | self.scene = scene 22 | } 23 | 24 | func createMonster() -> Entity { 25 | 26 | let monster = self.entityManager.createEntity() 27 | 28 | let (w,h) = (Double(scene.frame.width), Double(scene.frame.height)) 29 | let (x,y) = (w + 256,Double(arc4random()) % h) 30 | let sprite = SKSpriteNode(imageNamed: "monster") 31 | sprite.size = CGSizeMake(128,128) 32 | sprite.position = CGPointMake(CGFloat(x), CGFloat(y)) 33 | scene.addChild(sprite) 34 | 35 | // physics 36 | sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size) 37 | sprite.physicsBody.dynamic = true 38 | sprite.physicsBody.categoryBitMask = PhysicsCategory.Monster 39 | sprite.physicsBody.collisionBitMask = PhysicsCategory.None 40 | sprite.physicsBody.contactTestBitMask = PhysicsCategory.Projectile | PhysicsCategory.Player 41 | 42 | let renderComponent = RenderComponent(node: sprite) 43 | self.entityManager.addComponent(monster, c: renderComponent) 44 | 45 | var motionComponent = MotionComponent(destination: sprite.position.xAxis(), speed: 200) 46 | self.entityManager.addComponent(monster, c: motionComponent) 47 | 48 | 49 | let health = HealthComponent(currentHealth: 10, maxHealth: 10) 50 | self.entityManager.addComponent(monster, c: health) 51 | 52 | let collideWithPlayerRule = CollisionRule(hitCategory: PhysicsCategory.Player, damageGiven: 10, damageSustained:10) 53 | self.entityManager.addComponent(monster, c: CollisionComponent(collisionRules: [collideWithPlayerRule])) 54 | 55 | let deathDrama = DramaticDeath() { 56 | renderComponent.node.removeAllActions() 57 | renderComponent.node.physicsBody = nil 58 | renderComponent.node.runAction(self.scene.actionLibrary.death) 59 | } 60 | self.entityManager.addComponent(monster, c: deathDrama) 61 | 62 | self.entityManager.addComponent(monster, c: RenderHealthBarComponent()) 63 | 64 | return monster 65 | } 66 | 67 | func createPlayer() -> Entity { 68 | let sprite = SKSpriteNode(imageNamed: "ninja") 69 | sprite.size = CGSizeMake(128,128) 70 | sprite.xScale = -1 71 | sprite.position = CGPointMake(sprite.size.width, scene.frame.height/2) 72 | scene.addChild(sprite) 73 | 74 | // physics 75 | sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size) 76 | sprite.physicsBody.dynamic = true 77 | sprite.physicsBody.categoryBitMask = PhysicsCategory.Player 78 | sprite.physicsBody.collisionBitMask = PhysicsCategory.None 79 | sprite.physicsBody.contactTestBitMask = PhysicsCategory.Monster 80 | 81 | 82 | let player = self.entityManager.createEntity() 83 | let renderComponent = RenderComponent(node: sprite) 84 | let health = HealthComponent(currentHealth: 100, maxHealth: 100) 85 | 86 | let collideWithMonsterRule = CollisionRule(hitCategory:PhysicsCategory.Monster, damageGiven:0, damageSustained:10) 87 | self.entityManager.addComponent(player, c: CollisionComponent(collisionRules: [collideWithMonsterRule])) 88 | 89 | 90 | self.entityManager.addComponent(player, c: renderComponent) 91 | self.entityManager.addComponent(player, c: health) 92 | self.entityManager.addComponent(player, c: RenderHealthBarComponent()) 93 | return player 94 | } 95 | 96 | func createFire() -> Entity { 97 | 98 | let sprite = SKSpriteNode(imageNamed: "fire") 99 | scene.addChild(sprite) 100 | 101 | // physics 102 | sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size) 103 | sprite.physicsBody.dynamic = true 104 | sprite.physicsBody.categoryBitMask = PhysicsCategory.Projectile 105 | sprite.physicsBody.collisionBitMask = PhysicsCategory.None 106 | sprite.physicsBody.contactTestBitMask = PhysicsCategory.Monster 107 | 108 | let fire = self.entityManager.createEntity() 109 | let render = RenderComponent(node: sprite) 110 | let collideWithMonsterRule = CollisionRule(hitCategory: PhysicsCategory.Monster, damageGiven:5, damageSustained:1.0) 111 | let collision = CollisionComponent(collisionRules: [collideWithMonsterRule]) 112 | 113 | let health = HealthComponent(currentHealth: 1.0, maxHealth: 1.0) 114 | let decay = HealthDecayComponent(factor: 0.1) 115 | let motion = MotionComponent(destination: render.node.position, speed:250) 116 | 117 | self.entityManager.addComponent(fire, c: render) 118 | self.entityManager.addComponent(fire, c: collision) 119 | self.entityManager.addComponent(fire, c: health) 120 | self.entityManager.addComponent(fire, c: decay) 121 | self.entityManager.addComponent(fire, c: motion) 122 | 123 | return fire 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /gameninja/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 19/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | struct PhysicsCategory { 13 | static let None = (0) as UInt32 14 | static let Player = (1 << 0) as UInt32 15 | static let Monster = (1 << 1) as UInt32 16 | static let Projectile = (1 << 2) as UInt32 17 | static let Robot = (1 << 3) as UInt32 18 | } 19 | 20 | protocol RandomNumberGenerator { 21 | func random() -> Double 22 | } 23 | 24 | class ArcRandom : RandomNumberGenerator{ 25 | func random() -> Double { 26 | let r = arc4random() % 100 27 | return Double(Double(r)/100.0) 28 | } 29 | } 30 | 31 | 32 | class ActionLibrary { 33 | 34 | let weaponSound = SKAction.playSoundFileNamed("pew-pew-lei.caf", waitForCompletion: false) 35 | 36 | let moveBack = SKAction.moveByX(150, y: 0, duration: 0.3) 37 | let rotate = SKAction.rotateByAngle(-M_PI_2, duration: 0.3) 38 | let redtint = SKAction.colorizeWithColor(UIColor.redColor(), colorBlendFactor: 0.5, duration: 0.3) 39 | 40 | let fadeout = SKAction.fadeAlphaTo(0, duration: 1.0) 41 | let scale = SKAction.scaleBy(0.1, duration: 1.0) 42 | 43 | let remove = SKAction.removeFromParent() 44 | 45 | var rotate_tint: SKAction! 46 | var fade_scale: SKAction! 47 | var death: SKAction! 48 | 49 | // setup our hero 50 | var ninja1 = SKTexture(imageNamed: "ninja_throw") 51 | var ninja2 = SKTexture(imageNamed: "ninja") 52 | var ninjaThrow: SKAction! 53 | 54 | 55 | init(){ 56 | rotate_tint = SKAction.group([moveBack, rotate, redtint]) 57 | fade_scale = SKAction.group([fadeout, scale]) 58 | death = SKAction.sequence([rotate_tint, fade_scale, remove]) 59 | ninjaThrow = SKAction.animateWithTextures([ninja1, ninja2], timePerFrame: 0.2) 60 | } 61 | } 62 | 63 | extension CGPoint{ 64 | func translate(x: CGFloat, _ y: CGFloat) -> CGPoint { 65 | return CGPointMake(self.x + x, self.y + y) 66 | } 67 | func translateX(x: CGFloat) -> CGPoint { 68 | return CGPointMake(self.x + x, self.y) 69 | } 70 | func xAxis() -> CGPoint { 71 | return CGPointMake(0, self.y) 72 | } 73 | func yAxis() -> CGPoint { 74 | return CGPointMake(self.x, 0) 75 | } 76 | func translateY(y: CGFloat) -> CGPoint { 77 | return CGPointMake(self.x, self.y + y) 78 | } 79 | func addTo(a: CGPoint) -> CGPoint{ 80 | return CGPointMake(self.x+a.x, self.y+a.y) 81 | } 82 | func deltaTo(a: CGPoint) -> CGPoint{ 83 | return CGPointMake(self.x-a.x, self.y-a.y) 84 | } 85 | func multiplyBy(value:CGFloat) -> CGPoint{ 86 | return CGPointMake(self.x*value, self.y*value) 87 | } 88 | 89 | func length() -> CGFloat { 90 | return CGFloat(sqrt(CDouble( 91 | self.x*self.x + self.y*self.y 92 | ))) 93 | } 94 | func normalize() -> CGPoint { 95 | let l = self.length() 96 | return CGPointMake(self.x / l, self.y / l) 97 | } 98 | } 99 | 100 | extension CGSize { 101 | func reduceBy(amount:CGFloat) -> CGSize { 102 | return CGSizeMake(self.width * amount, self.height * amount) 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /gameninja/GameOverScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameOverScene.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 05/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | extension CGSize { 12 | func mid() -> CGPoint { 13 | return CGPointMake(self.width/2, self.height/2) 14 | } 15 | } 16 | 17 | class GameOverScene: SKScene { 18 | 19 | 20 | init(size: CGSize, won: Bool) { 21 | super.init(size: size) 22 | 23 | // self.backgroundColor = UIColor.blackColor() 24 | // 25 | // let message = "winning state \(won)" 26 | // 27 | // 28 | // let label = SKLabelNode(text: message) 29 | // label.fontSize = 40 30 | // label.fontColor = UIColor.whiteColor() 31 | // label.position = self.size.mid() 32 | // self.addChild(label) 33 | // 34 | // 35 | // // display main game scene after 3 seconds 36 | // let sequence = [ 37 | // SKAction.waitForDuration(3.0), 38 | // SKAction.runBlock(){ 39 | // let transition = SKTransition.flipHorizontalWithDuration(0.5) 40 | // let gameScene = GameScene(size: self.size) 41 | // self.view.presentScene(gameScene, transition: transition) 42 | // } 43 | // ] 44 | // 45 | // self.runAction(SKAction.sequence(sequence)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /gameninja/GameScene.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/GameScene.sks -------------------------------------------------------------------------------- /gameninja/GameScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 05/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | import Darwin 11 | 12 | class GameScene: SKScene { 13 | 14 | let generator = ArcRandom() 15 | let actionLibrary = ActionLibrary() 16 | var lastSpawnTimeInterval:CFTimeInterval = 0 17 | var lastUpdateTimeInterval:CFTimeInterval = 0 18 | 19 | var player:Entity! = nil 20 | var entityFactory:EntityFactory! = nil 21 | var entityManager:EntityManager! = nil 22 | 23 | var motionSystem:MotionSystem! = nil 24 | var physicsSystem:PhysicsSystem! = nil 25 | var healthSystem:HealthSystem! = nil 26 | var renderSystem:RenderSystem! = nil 27 | 28 | 29 | override func didMoveToView(view: SKView) { 30 | /* Setup your scene here */ 31 | entityManager = EntityManager() 32 | 33 | motionSystem = MotionSystem(entityManager: entityManager) 34 | physicsSystem = PhysicsSystem(entityManager: entityManager) 35 | healthSystem = HealthSystem(entityManager: entityManager) 36 | renderSystem = RenderSystem(entityManager: entityManager) 37 | 38 | entityFactory = EntityFactory(entityManager: entityManager, scene: self) 39 | 40 | 41 | // add the player 42 | player = entityFactory.createPlayer() 43 | 44 | // setup physics 45 | self.physicsWorld.gravity = CGVectorMake(0, 0) 46 | self.physicsWorld.contactDelegate = physicsSystem 47 | 48 | } 49 | 50 | 51 | func updateLastTimeUpate(time: CFTimeInterval){ 52 | lastSpawnTimeInterval += time 53 | if lastSpawnTimeInterval > 1 { 54 | lastSpawnTimeInterval = 0 55 | spawnMonster() 56 | } 57 | } 58 | 59 | func spawnMonster() { 60 | // add a monster every 1 second 61 | let monster = entityFactory.createMonster() 62 | 63 | // modify target location based on players location 64 | let motion = entityManager.getComponent(monster, type: ComponentType.Motion) as MotionComponent 65 | let monsterNode = entityManager.getComponent(monster, type: ComponentType.Render) as RenderComponent 66 | if let playerNode = (entityManager.getComponent(player, type: ComponentType.Render) as? RenderComponent) { 67 | motion.destination = playerNode.node.position 68 | } else { 69 | motion.destination = monsterNode.node.position.xAxis() 70 | } 71 | } 72 | 73 | 74 | override func update(currentTime: CFTimeInterval) { 75 | /* Called before each frame is rendered */ 76 | // Handle time delta. 77 | // If we drop below 60fps, we still want everything to move the same distance. 78 | var timeSinceLast = currentTime - self.lastUpdateTimeInterval; 79 | self.lastUpdateTimeInterval = currentTime; 80 | if (timeSinceLast > 1) { // more than a second since last update 81 | timeSinceLast = 1.0 / 60.0; 82 | self.lastUpdateTimeInterval = currentTime; 83 | } 84 | updateLastTimeUpate(timeSinceLast) 85 | 86 | motionSystem.update(timeSinceLast) 87 | physicsSystem.update(timeSinceLast) 88 | healthSystem.update(timeSinceLast) 89 | renderSystem.update(timeSinceLast) 90 | } 91 | 92 | 93 | override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) { 94 | let playerNode = self.entityManager.getComponent(player, type: ComponentType.Render) as? RenderComponent 95 | if playerNode { 96 | let touch:UITouch = touches.anyObject() as UITouch 97 | let fire = self.entityFactory.createFire() 98 | 99 | let fireNode = self.entityManager.getComponent(fire, type: ComponentType.Render) as RenderComponent 100 | 101 | let fireHealth = self.entityManager.getComponent(fire, type: ComponentType.Health) as HealthComponent 102 | let fireMotion = self.entityManager.getComponent(fire, type: ComponentType.Motion) as MotionComponent 103 | 104 | fireNode.node.position = playerNode!.node.position.translate(65, -40) 105 | let offset = touch.locationInNode(self).deltaTo(fireNode.node.position) 106 | let destination = fireNode.node.position.addTo( offset.normalize().multiplyBy(1000) ) 107 | fireMotion.destination = destination 108 | 109 | playerNode!.node.runAction(actionLibrary.ninjaThrow) 110 | self.runAction(actionLibrary.weaponSound) 111 | } 112 | } 113 | 114 | 115 | } 116 | -------------------------------------------------------------------------------- /gameninja/GameSpriteNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameSpriteNode.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 20/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | import CoreGraphics 12 | import QuartzCore 13 | 14 | class GameSpriteNode: SKSpriteNode { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /gameninja/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 05/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | import AVFoundation 12 | 13 | extension SKNode { 14 | class func unarchiveFromFile(file : NSString) -> SKNode? { 15 | 16 | let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") 17 | 18 | var sceneData = NSData.dataWithContentsOfFile(path, options: .DataReadingMappedIfSafe, error: nil) 19 | var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData) 20 | 21 | archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene") 22 | let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene 23 | archiver.finishDecoding() 24 | return scene 25 | } 26 | } 27 | 28 | class GameViewController: UIViewController { 29 | 30 | var musicPlayer: AVAudioPlayer! 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | 35 | if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene { 36 | // Configure the view. 37 | let skView = self.view as SKView 38 | skView.showsFPS = true 39 | skView.showsNodeCount = true 40 | 41 | 42 | /* Sprite Kit applies additional optimizations to improve rendering performance */ 43 | skView.ignoresSiblingOrder = true 44 | 45 | /* Set the scale mode to scale to fit the window */ 46 | scene.scaleMode = .AspectFill 47 | 48 | skView.presentScene(scene) 49 | } 50 | } 51 | 52 | 53 | 54 | // override func viewDidLayoutSubviews() { 55 | // super.viewDidLayoutSubviews() 56 | // let music = NSBundle.mainBundle().URLForResource("background-music-aac", withExtension: "caf") 57 | // musicPlayer = AVAudioPlayer(contentsOfURL: music, error: nil) 58 | // musicPlayer.numberOfLoops = -1; // forever 59 | // musicPlayer.prepareToPlay() 60 | // musicPlayer.volume = 0.5 61 | // musicPlayer.play() 62 | // } 63 | 64 | override func shouldAutorotate() -> Bool { 65 | return true 66 | } 67 | 68 | override func supportedInterfaceOrientations() -> Int { 69 | if UIDevice.currentDevice().userInterfaceIdiom == .Phone { 70 | return Int(UIInterfaceOrientationMask.AllButUpsideDown.toRaw()) 71 | } else { 72 | return Int(UIInterfaceOrientationMask.All.toRaw()) 73 | } 74 | } 75 | 76 | override func didReceiveMemoryWarning() { 77 | super.didReceiveMemoryWarning() 78 | // Release any cached data, images, etc that aren't in use. 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /gameninja/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/fire.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "fire.png" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/fire.imageset/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/Images.xcassets/fire.imageset/fire.png -------------------------------------------------------------------------------- /gameninja/Images.xcassets/land.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "land.png" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/land.imageset/land.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/Images.xcassets/land.imageset/land.png -------------------------------------------------------------------------------- /gameninja/Images.xcassets/monster.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "monster.png" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/monster.imageset/monster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/Images.xcassets/monster.imageset/monster.png -------------------------------------------------------------------------------- /gameninja/Images.xcassets/ninja.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "ninja.png" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/ninja.imageset/ninja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/Images.xcassets/ninja.imageset/ninja.png -------------------------------------------------------------------------------- /gameninja/Images.xcassets/robot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "brown.png" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /gameninja/Images.xcassets/robot.imageset/brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/Images.xcassets/robot.imageset/brown.png -------------------------------------------------------------------------------- /gameninja/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | benzi.swift.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /gameninja/Systems.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Systems.swift 3 | // gameninja 4 | // 5 | // Created by Benzi on 19/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | 13 | // MARK: Systems ------------------------------------------ 14 | 15 | class System : NSObject { 16 | var entityManager:EntityManager 17 | 18 | init(entityManager:EntityManager) { 19 | self.entityManager = entityManager 20 | } 21 | 22 | func update(dt:Double) {} 23 | } 24 | 25 | 26 | class MotionSystem : System { 27 | override func update(dt: Double) { 28 | for e in self.entityManager.getEntitiesWithComponent(ComponentType.Motion){ 29 | let motion = self.entityManager.getComponent(e, type: ComponentType.Motion) as MotionComponent 30 | if !motion.inMotion { 31 | motion.inMotion = true 32 | // animate to target location 33 | let render = self.entityManager.getComponent(e, type: ComponentType.Render) as RenderComponent 34 | let move = SKAction.moveTo(motion.destination, duration:1000/motion.speed) 35 | render.node.runAction(move) 36 | } 37 | } 38 | } 39 | } 40 | 41 | 42 | class PhysicsSystem : System, SKPhysicsContactDelegate { 43 | 44 | func findEntityWithNode(node:SKNode) -> Entity? { 45 | for entity in self.entityManager.getEntitiesWithComponent(ComponentType.Collision){ 46 | if let render = self.entityManager.getComponent(entity, type: ComponentType.Render) as? RenderComponent { 47 | if render.node == node { 48 | return entity 49 | } 50 | } 51 | } 52 | return nil 53 | } 54 | 55 | func applyCollision(hitter:Entity, hittee:Entity, hitteeCategory:UInt32){ 56 | if let collision = self.entityManager.getComponent(hitter, type: ComponentType.Collision) as? CollisionComponent { 57 | let hitteeHealth = self.entityManager.getComponent(hittee, type: ComponentType.Health) as? HealthComponent 58 | let hitterHealth = self.entityManager.getComponent(hitter, type: ComponentType.Health) as? HealthComponent 59 | for rule in collision.rules { 60 | if rule.appliesTo(hitteeCategory){ 61 | if hitteeHealth { 62 | hitteeHealth!.currentHealth -= rule.damageGiven 63 | } 64 | if hitterHealth { 65 | hitterHealth!.currentHealth -= rule.damageSustained 66 | } 67 | } 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | func didBeginContact(contact: SKPhysicsContact!) { 75 | 76 | let a = findEntityWithNode(contact.bodyA.node) 77 | let b = findEntityWithNode(contact.bodyB.node) 78 | 79 | if a && b { 80 | // a hits b 81 | applyCollision(a!, hittee: b!, hitteeCategory: contact.bodyB.categoryBitMask) 82 | // b hits a 83 | applyCollision(b!, hittee: a!, hitteeCategory: contact.bodyA.categoryBitMask) 84 | } 85 | } 86 | } 87 | 88 | 89 | 90 | class HealthSystem : System { 91 | 92 | var lastDecayTimeInterval:CFTimeInterval = 0 93 | 94 | func updateAlive(){ 95 | // for all entites that have 0 health we remove them and kill them 96 | let entities = self.entityManager.getEntitiesWithComponent(ComponentType.Health) 97 | for e in entities { 98 | let health = self.entityManager.getComponent(e, type: ComponentType.Health) as HealthComponent 99 | if (health.isAlive && health.currentHealth <= 0.0){ 100 | health.isAlive = false 101 | } 102 | } 103 | } 104 | 105 | func decayHealth(dt:Double){ 106 | // decay health every 0.5 seconds 107 | lastDecayTimeInterval += dt 108 | if lastDecayTimeInterval > 0.5 { 109 | lastDecayTimeInterval = 0 110 | let entities = self.entityManager.getEntitiesWithComponent(ComponentType.HealthDecay) 111 | for e in entities { 112 | let decay = self.entityManager.getComponent(e, type: ComponentType.HealthDecay) as HealthDecayComponent 113 | let health = self.entityManager.getComponent(e, type: ComponentType.Health) as HealthComponent 114 | health.currentHealth -= decay.factor 115 | } 116 | } 117 | } 118 | 119 | override func update(dt: Double) { 120 | decayHealth(dt) 121 | updateAlive() 122 | } 123 | } 124 | 125 | 126 | class RenderSystem : System { 127 | 128 | override func update(dt: Double) { 129 | 130 | // for all entities that has a health bar component, draw a health bar 131 | let entities = self.entityManager.getEntitiesWithComponent(ComponentType.HealthBar) 132 | for e in entities { 133 | let render = self.entityManager.getComponent(e, type: ComponentType.Render) as RenderComponent 134 | let health = self.entityManager.getComponent(e, type: ComponentType.Health) as HealthComponent 135 | 136 | render.node.removeAllChildren() 137 | 138 | let healthBarWidth = render.node.size.width * 0.8 139 | let healthBarHeight = 10.0 140 | let widthOfHealth = (healthBarWidth - 2.0) * health.currentHealth / health.maxHealth 141 | 142 | let healthBarColor = UIColor(red: 1.0 - health.percent, green: health.percent, blue: 0, alpha: 1.0) 143 | 144 | //create the outline for the health bar 145 | let outlineRectSize = CGSizeMake(healthBarWidth-1.0,healthBarHeight-1.0); 146 | UIGraphicsBeginImageContextWithOptions(outlineRectSize, false, 0.0) 147 | let healthBarContext = UIGraphicsGetCurrentContext() 148 | //Drawing the outline for the health bar 149 | 150 | let spriteOutlineRect = CGRectMake(0.0, 0.0, healthBarWidth-1.0, healthBarHeight-1.0) 151 | CGContextSetStrokeColorWithColor(healthBarContext, UIColor.whiteColor()!.CGColor!) 152 | CGContextSetLineWidth(healthBarContext, 1.0) 153 | CGContextAddRect(healthBarContext, spriteOutlineRect) 154 | CGContextStrokePath(healthBarContext) 155 | 156 | //Fill the health bar with a filled rectangle 157 | let spriteFillRect = CGRectMake(0.5, 0.5, widthOfHealth, outlineRectSize.height-1.0) 158 | CGContextSetFillColorWithColor(healthBarContext, healthBarColor.CGColor!) 159 | CGContextSetStrokeColorWithColor(healthBarContext, UIColor.blackColor()!.CGColor!); 160 | CGContextSetLineWidth(healthBarContext, 1.0) 161 | CGContextFillRect(healthBarContext, spriteFillRect) 162 | 163 | //Generate a sprite image of the two pieces for display 164 | let spriteImage = UIGraphicsGetImageFromCurrentImageContext() 165 | UIGraphicsEndImageContext() 166 | 167 | let texture = SKTexture(image: spriteImage) 168 | texture.filteringMode = SKTextureFilteringMode.Linear 169 | let healthNode = SKSpriteNode(texture: texture) 170 | healthNode.position = CGPointMake(0, render.node.size.height/2 + 20) 171 | healthNode.anchorPoint = CGPointMake(0.5, 0.5) 172 | 173 | render.node.addChild(healthNode) 174 | } 175 | 176 | 177 | // for all dead nodes, update the tree 178 | let entities2 = self.entityManager.getEntitiesWithComponent(ComponentType.Health) 179 | for e in entities2 { 180 | let health = self.entityManager.getComponent(e, type: ComponentType.Health) as HealthComponent 181 | if !health.isAlive { 182 | // if this entity has a custom dramatic death 183 | if let death = self.entityManager.getComponent(e, type: ComponentType.SpecialDeath) as? DramaticDeath { 184 | death.action() 185 | } else if let render = self.entityManager.getComponent(e, type: ComponentType.Render) as? RenderComponent { 186 | render.node.removeAllActions() 187 | render.node.runAction(SKAction.removeFromParent()) 188 | } 189 | self.entityManager.removeEntity(e) 190 | } 191 | } 192 | } 193 | 194 | } -------------------------------------------------------------------------------- /gameninja/background-music-aac.caf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/background-music-aac.caf -------------------------------------------------------------------------------- /gameninja/pew-pew-lei.caf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/gameninja/pew-pew-lei.caf -------------------------------------------------------------------------------- /gameninjaTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | benzi.swift.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /gameninjaTests/gameninjaTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // gameninjaTests.swift 3 | // gameninjaTests 4 | // 5 | // Created by Benzi on 05/06/14. 6 | // Copyright (c) 2014 Benzi Ahamed. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class gameninjaTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenziAhamed/swift-game-ninja/49dab0d3df1ff335b887e525c1f5eefd1e400462/screen.png --------------------------------------------------------------------------------