├── DynamicWater.gif ├── Demo ├── DemoSource │ ├── rock.png │ ├── sky.png │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── GameScene.sks │ ├── GameViewController.h │ ├── GameScene.h │ ├── Rock.h │ ├── SettingsTitleCell.h │ ├── AppDelegate.h │ ├── main.m │ ├── SettingsSliderCell.h │ ├── Rock.m │ ├── SettingsCellInfo.m │ ├── SettingsTitleCell.m │ ├── SettingsCellInfo.h │ ├── SettingsView.h │ ├── GameViewController.m │ ├── SettingsSliderCell.m │ ├── Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── DynamicWaterNode.h.orig │ ├── AppDelegate.m │ ├── SettingsSliderCell.xib │ ├── SettingsTitleCell.xib │ ├── SettingsView.xib │ ├── GameScene.m │ └── SettingsView.m └── DynamicWater.xcodeproj │ ├── xcuserdata │ └── stevebarnegren.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── DynamicWater.xcscheme │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── stevebarnegren.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ └── project.pbxproj ├── SBDynamicWaterNode ├── Droplet.png ├── Droplets.fsh ├── SBDynamicWaterNode.h └── SBDynamicWaterNode.m ├── Xcode.gitignore ├── LICENSE ├── SBDynamicWaterNode.podspec └── README.md /DynamicWater.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteveBarnegren/SBDynamicWaterNode/HEAD/DynamicWater.gif -------------------------------------------------------------------------------- /Demo/DemoSource/rock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteveBarnegren/SBDynamicWaterNode/HEAD/Demo/DemoSource/rock.png -------------------------------------------------------------------------------- /Demo/DemoSource/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteveBarnegren/SBDynamicWaterNode/HEAD/Demo/DemoSource/sky.png -------------------------------------------------------------------------------- /Demo/DemoSource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/DemoSource/GameScene.sks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteveBarnegren/SBDynamicWaterNode/HEAD/Demo/DemoSource/GameScene.sks -------------------------------------------------------------------------------- /SBDynamicWaterNode/Droplet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteveBarnegren/SBDynamicWaterNode/HEAD/SBDynamicWaterNode/Droplet.png -------------------------------------------------------------------------------- /Demo/DynamicWater.xcodeproj/xcuserdata/stevebarnegren.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Demo/DynamicWater.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/DynamicWater.xcodeproj/project.xcworkspace/xcuserdata/stevebarnegren.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteveBarnegren/SBDynamicWaterNode/HEAD/Demo/DynamicWater.xcodeproj/project.xcworkspace/xcuserdata/stevebarnegren.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo/DemoSource/GameViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.h 3 | // DynamicWater 4 | // 5 | 6 | // Copyright (c) 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface GameViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SBDynamicWaterNode/Droplets.fsh: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | 4 | vec4 texture = texture2D(u_texture, v_tex_coord); 5 | if (texture.a > 0.8) { 6 | texture = u_colour; 7 | } 8 | else{ 9 | texture = vec4(0.0, 0.0, 0.0, 0.0); 10 | } 11 | 12 | gl_FragColor = texture; 13 | 14 | } -------------------------------------------------------------------------------- /Demo/DemoSource/GameScene.h: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.h 3 | // DynamicWater 4 | // 5 | 6 | // Copyright (c) 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GameScene : SKScene 12 | 13 | @property float splashWidth; 14 | @property float splashForceMultiplier; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Demo/DemoSource/Rock.h: -------------------------------------------------------------------------------- 1 | // 2 | // Rock.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 08/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Rock : SKSpriteNode 12 | 13 | @property BOOL isAboveWater; 14 | @property CGPoint velocity; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsTitleCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsTitleCell.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SettingsTitleCell : UITableViewCell 12 | 13 | -(void)setTitle:(NSString*)title; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/DemoSource/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo/DemoSource/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsSliderCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsSliderCell.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SettingsCellInfo.h" 11 | 12 | @interface SettingsSliderCell : UITableViewCell 13 | 14 | @property (nonatomic, strong) NSString *title; 15 | 16 | -(void)setupWithInfo:(SettingsCellSliderInfo*)info; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Xcode.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | -------------------------------------------------------------------------------- /Demo/DemoSource/Rock.m: -------------------------------------------------------------------------------- 1 | // 2 | // Rock.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 08/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "Rock.h" 10 | 11 | @interface Rock () 12 | @end 13 | 14 | 15 | @implementation Rock 16 | 17 | -(instancetype)initWithImageNamed:(NSString *)name{ 18 | 19 | if (self = [super initWithImageNamed:name]) { 20 | self.isAboveWater = YES; 21 | self.velocity = CGPointZero; 22 | } 23 | return self; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsCellInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsCellInfo.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "SettingsCellInfo.h" 10 | 11 | @implementation SettingsCellInfo 12 | +(SettingsCellInfo*)cellInfoWithTitle:(NSString*)title{ 13 | SettingsCellInfo *info = [[SettingsCellInfo alloc ]init]; 14 | info.title = title; 15 | return info; 16 | } 17 | 18 | @end 19 | 20 | @implementation SettingsCellSliderInfo 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsTitleCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsTitleCell.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "SettingsTitleCell.h" 10 | 11 | @interface SettingsTitleCell () 12 | @property (nonatomic, weak) IBOutlet UILabel *label; 13 | @end 14 | 15 | @implementation SettingsTitleCell 16 | 17 | - (void)awakeFromNib { 18 | // Initialization code 19 | } 20 | 21 | -(void)setTitle:(NSString*)title{ 22 | self.label.text = title; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Demo/DynamicWater.xcodeproj/xcuserdata/stevebarnegren.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DynamicWater.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 67DA19381C8DE6C400BB9382 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsCellInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsCellInfo.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SettingsCellInfo : NSObject 12 | @property (nonatomic, strong) NSString *title; 13 | +(SettingsCellInfo*)cellInfoWithTitle:(NSString*)title; 14 | @end 15 | 16 | @interface SettingsCellSliderInfo : SettingsCellInfo 17 | @property float startValue; 18 | @property float endValue; 19 | @property (nonatomic, copy) float (^getCurrentValue)(); 20 | @property (nonatomic, copy) void (^updateCallback)(float newValue); 21 | @property BOOL showValueReadout; 22 | @end 23 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsView.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SBDynamicWaterNode.h" 11 | 12 | @class GameScene; 13 | 14 | @protocol SettingsViewDelegate; 15 | 16 | @interface SettingsView : UIView 17 | @property (nonatomic, weak) id delegate; 18 | 19 | @property (nonatomic, weak) GameScene *gameScene; 20 | @property (nonatomic, weak) SBDynamicWaterNode *waterNode; 21 | +(id)instanceFromNib; 22 | 23 | @end 24 | 25 | @protocol SettingsViewDelegate 26 | -(void)settingsViewShouldClose:(SettingsView*)settingsView; 27 | -(void)settingsViewWantsRestoreDefaultValues:(SettingsView*)settingsView; 28 | @end 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Steve Barnegren 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /SBDynamicWaterNode.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SBDynamicWaterNode" 3 | s.version = "1.0.1" 4 | s.summary = "Physical water simulation for SpriteKit" 5 | 6 | s.description = <<-DESC 7 | Physical water simulation for SpriteKit with demo application 8 | DESC 9 | 10 | s.homepage = "https://github.com/SteveBarnegren/DynamicWaterNode" 11 | # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 12 | s.license = 'MIT' 13 | s.author = { "Steve Barnegren" => "steve.barnegren@gmail.com" } 14 | s.source = { :git => "https://github.com/SteveBarnegren/SBDynamicWaterNode.git", :tag => s.version.to_s } 15 | s.social_media_url = 'https://twitter.com/stevebarnegren' 16 | 17 | s.platform = :ios, '8.0' 18 | s.requires_arc = true 19 | 20 | s.source_files = 'SBDynamicWaterNode/**/*.{h,m}' 21 | s.resources = 'SBDynamicWaterNode/**/*.{png,fsh}' 22 | #s.resource_bundles = { 23 | # 'SBDynamicWaterNode' => ['SBDynamicWaterNode/*.{png,fsh}'] 24 | #} 25 | 26 | # s.public_header_files = 'Pod/Classes/**/*.h' 27 | s.frameworks = 'SpriteKit' 28 | end 29 | -------------------------------------------------------------------------------- /Demo/DemoSource/Assets.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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /Demo/DemoSource/GameViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright (c) 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "GameViewController.h" 10 | #import "GameScene.h" 11 | 12 | @implementation GameViewController 13 | 14 | - (void)viewDidLoad 15 | { 16 | [super viewDidLoad]; 17 | 18 | // Configure the view. 19 | SKView * skView = (SKView *)self.view; 20 | skView.showsFPS = YES; 21 | skView.showsNodeCount = YES; 22 | /* Sprite Kit applies additional optimizations to improve rendering performance */ 23 | skView.ignoresSiblingOrder = YES; 24 | 25 | // Create and configure the scene. 26 | GameScene *scene = [GameScene nodeWithFileNamed:@"GameScene"]; 27 | scene.scaleMode = SKSceneScaleModeAspectFill; 28 | 29 | // Present the scene. 30 | [skView presentScene:scene]; 31 | } 32 | 33 | - (BOOL)shouldAutorotate 34 | { 35 | return YES; 36 | } 37 | 38 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations 39 | { 40 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { 41 | return UIInterfaceOrientationMaskAllButUpsideDown; 42 | } else { 43 | return UIInterfaceOrientationMaskAll; 44 | } 45 | } 46 | 47 | - (void)didReceiveMemoryWarning 48 | { 49 | [super didReceiveMemoryWarning]; 50 | // Release any cached data, images, etc that aren't in use. 51 | } 52 | 53 | - (BOOL)prefersStatusBarHidden { 54 | return YES; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsSliderCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsSliderCell.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "SettingsSliderCell.h" 10 | 11 | @interface SettingsSliderCell () 12 | @property (nonatomic, strong) SettingsCellSliderInfo *info; 13 | 14 | @property (nonatomic, weak) IBOutlet UILabel *label; 15 | @property (nonatomic, weak) IBOutlet UISlider *slider; 16 | 17 | @end 18 | 19 | @implementation SettingsSliderCell 20 | 21 | - (void)awakeFromNib { 22 | // Initialization code 23 | } 24 | 25 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 26 | [super setSelected:selected animated:animated]; 27 | 28 | // Configure the view for the selected state 29 | } 30 | 31 | -(void)setupWithInfo:(SettingsCellSliderInfo*)info{ 32 | self.info = info; 33 | 34 | self.slider.minimumValue = info.startValue; 35 | self.slider.maximumValue = info.endValue; 36 | self.slider.continuous = YES; 37 | self.slider.value = info.getCurrentValue(); 38 | 39 | [self updateLabel]; 40 | } 41 | 42 | -(IBAction)sliderValueChanged:(UISlider*)slider{ 43 | NSLog(@"slider value changed"); 44 | self.info.updateCallback(slider.value); 45 | [self updateLabel]; 46 | } 47 | 48 | 49 | -(void)updateLabel{ 50 | 51 | NSString *text = self.info.title; 52 | if (self.info.showValueReadout) { 53 | text = [text stringByAppendingString:[NSString stringWithFormat:@" (%g)", self.slider.value]]; 54 | } 55 | self.label.text = text; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Demo/DemoSource/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Demo/DemoSource/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 | -------------------------------------------------------------------------------- /Demo/DemoSource/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DynamicWaterNode 2 | 3 | [![Pod Version](http://img.shields.io/cocoapods/v/SBDynamicWaterNode.svg?style=flat)](http://cocoadocs.org/docsets/SBDynamicWaterNode/) 4 | [![Pod Platform](http://img.shields.io/cocoapods/p/SBDynamicWaterNode.svg?style=flat)](http://cocoadocs.org/docsets/SBDynamicWaterNode/) 5 | [![Pod License](http://img.shields.io/cocoapods/l/SBDynamicWaterNode.svg?style=flat)](http://cocoadocs.org/docsets/SBDynamicWaterNode/) 6 | 7 | 2D physical water simulation for SpriteKit 8 | 9 | ![GIF](https://github.com/SteveBarnegren/DynamicWaterNode/raw/master/DynamicWater.gif) 10 | 11 | # How to use 12 | 13 | Add DynamicWaterNode.h, DynamicWaterNode.m, Droplet.png and Droplets.fsh to your project 14 | 15 | Add to your scene: 16 | 17 | ``` 18 | - (void)didMoveToView:(SKView *)view { 19 | self.waterNode = [[DynamicWaterNode alloc]initWithWidth:self.size.width 20 | numJoints:100 21 | surfaceHeight:kSurfaceHeight 22 | fillColour:[UIColor colorWithRed:0 green:0 blue:1 alpha:0.5]]; 23 | self.waterNode.position = CGPointMake(self.size.width/2, 0); 24 | [self addChild:self.waterNode]; 25 | 26 | } 27 | ``` 28 | 29 | Step the simulation in your scene’s update method. See demo project for an example of a fixed time step implementation 30 | 31 | ``` 32 | [self.waterNode update:dt]; 33 | ``` 34 | 35 | Call render: after the simulation has been updated. Only call render once each frame. 36 | 37 | ``` 38 | [self.waterNode render]; 39 | ``` 40 | 41 | Making splashes: 42 | 43 | ``` 44 | [self.waterNode splashAtX:100 force:20 width:20]; 45 | ``` 46 | 47 | Various properties of DynamicWaterNode can be changed to control the feel of the water. The demo project includes a settings screen where you can alter these to see their effect. 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Demo/DemoSource/DynamicWaterNode.h.orig: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicWaterNode.h 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DynamicWaterNode : SKNode 12 | 13 | @property float surfaceHeight; 14 | @property (nonatomic) float tension; 15 | @property (nonatomic) float damping; 16 | @property float spread; 17 | 18 | #pragma mark - Init 19 | /** 20 | Designated Initialiser. 21 | @param width The width of the water 22 | @param numJoints The number of joints used to simulate the waves. More joints will result in a smoother wave curve, fewer joints will look more jagged, but may may improve performance. 23 | @param surfaceHeight the height of the water's surface 24 | @param fillColour The colour of the water 25 | */ 26 | -(instancetype)initWithWidth:(float)width numJoints:(NSInteger)numJoints surfaceHeight:(float)surfaceHeight fillColour:(UIColor*)fillColour; 27 | 28 | #pragma mark - Set Defaults 29 | /** Reset simulation variables to defaults */ 30 | -(void)setDefaultValues; 31 | 32 | #pragma mark - Update 33 | /** 34 | Step the time of the simulation 35 | @param dt: delta time since last update 36 | */ 37 | -(void)update:(CFTimeInterval)dt; 38 | 39 | <<<<<<< HEAD 40 | -(void)renderWater; 41 | ======= 42 | >>>>>>> master 43 | 44 | #pragma mark - Splash 45 | /** 46 | Make a splash (width will be 0) 47 | @param xLocation The X location to make the splash at 48 | @param force The force of the splash 49 | */ 50 | -(void)splashAtX:(float)xLocation force:(CGFloat)force; 51 | /** 52 | Make a splash 53 | @param xLocation The X location to make the splash at 54 | @param force The force of the splash 55 | @param width the width of the splash 56 | */ 57 | -(void)splashAtX:(float)xLocation force:(CGFloat)force width:(float)width; 58 | 59 | #pragma mark - Render 60 | /** 61 | Render the water. Only call this once per frame. You can still call update to update the simulation multiple times per frame (eg. fixed time-step). 62 | */ 63 | -(void)render; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Demo/DemoSource/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // 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. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /SBDynamicWaterNode/SBDynamicWaterNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // SBDynamicWaterNode.h 3 | // SBDynamicWaterNode 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SBDynamicWaterNode : SKNode 12 | 13 | /** Height of the water's surface */ 14 | @property float surfaceHeight; 15 | /** Tension the water, shoud probably be less than damping */ 16 | @property (nonatomic) float tension; 17 | /** Tension the water, shoud probably be greater than damping */ 18 | @property (nonatomic) float damping; 19 | /** Controls how fast/far waves propogate across water surface */ 20 | @property float spread; 21 | /** The amount of force applied to splash droplets */ 22 | @property float dropletsForce; 23 | /** Higher values will result splahes producing more water droplets */ 24 | @property float dropletsDensity; 25 | /** Size of water droplets */ 26 | @property float dropletSize; 27 | 28 | #pragma mark - Init 29 | /** 30 | Designated Initialiser. 31 | @param width The width of the water 32 | @param numJoints The number of joints used to simulate the waves. More joints will result in a smoother wave curve, fewer joints will look more jagged, but may may improve performance. 33 | @param surfaceHeight the height of the water's surface 34 | @param fillColour The colour of the water 35 | */ 36 | -(instancetype)initWithWidth:(float)width numJoints:(NSInteger)numJoints surfaceHeight:(float)surfaceHeight fillColour:(UIColor*)fillColour; 37 | 38 | #pragma mark - Set Defaults 39 | /** Reset simulation variables to defaults */ 40 | -(void)setDefaultValues; 41 | 42 | #pragma mark - Colour 43 | /** Set the water colour */ 44 | -(void)setColour:(UIColor*)colour; 45 | 46 | #pragma mark - Update 47 | /** 48 | Step the time of the simulation 49 | @param dt: delta time since last update 50 | */ 51 | -(void)update:(CFTimeInterval)dt; 52 | 53 | 54 | #pragma mark - Splash 55 | /** 56 | Make a splash 57 | @param xLocation: Location of the splash 58 | @param force: Force of the splash 59 | */ 60 | -(void)splashAtX:(float)xLocation force:(CGFloat)force; 61 | /** 62 | Make a splash 63 | @param xLocation: Location of the splash 64 | @param force: Force of the splash 65 | @param width: The width of the splash. Set to higher values to simulate objects with a larger surface area 66 | */ 67 | -(void)splashAtX:(float)xLocation force:(CGFloat)force width:(float)width; 68 | 69 | #pragma mark - Render 70 | /** 71 | Render the water. Only call this once per frame. You can still call update to update the simulation multiple times per frame (eg. fixed time-step). 72 | */ 73 | -(void)render; 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Demo/DynamicWater.xcodeproj/xcuserdata/stevebarnegren.xcuserdatad/xcschemes/DynamicWater.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsSliderCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsTitleCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Demo/DemoSource/GameScene.m: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright (c) 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "GameScene.h" 10 | #import "SBDynamicWaterNode.h" 11 | #import "Rock.h" 12 | #import "SettingsView.h" 13 | 14 | #define kFixedTimeStep (1.0f/500) 15 | 16 | #define kSurfaceHeight 235 17 | 18 | typedef enum : NSUInteger { 19 | ZPositionSky, 20 | ZPositionRock, 21 | ZPositionWater, 22 | } ZPositions; 23 | 24 | @interface GameScene () 25 | @property (nonatomic, strong) SettingsView *settingsView; 26 | 27 | @property (nonatomic, strong) SKSpriteNode *skySprite; 28 | @property (nonatomic, strong) SBDynamicWaterNode *waterNode; 29 | 30 | @property CFTimeInterval lastFrameTime; 31 | @property BOOL hasReferenceFrameTime; 32 | 33 | @property (nonatomic, strong) NSMutableArray *rocks; 34 | 35 | @end 36 | 37 | @implementation GameScene 38 | 39 | -(void)didMoveToView:(SKView *)view { 40 | 41 | self.rocks = [[NSMutableArray alloc]init]; 42 | 43 | // Sky 44 | self.skySprite = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:@"sky"]]; 45 | self.skySprite.xScale = self.size.width/self.skySprite.texture.size.width; 46 | self.skySprite.yScale = self.size.height/self.skySprite.texture.size.height; 47 | self.skySprite.position = CGPointMake(self.size.width/2, self.size.height/2); 48 | self.skySprite.zPosition = ZPositionSky; 49 | [self addChild:self.skySprite]; 50 | 51 | // Water 52 | self.waterNode = [[SBDynamicWaterNode alloc]initWithWidth:self.size.width 53 | numJoints:100 54 | surfaceHeight:kSurfaceHeight 55 | fillColour:[UIColor colorWithRed:0 green:0 blue:1 alpha:0.5]]; 56 | self.waterNode.position = CGPointMake(self.size.width/2, 0); 57 | self.waterNode.zPosition = ZPositionWater; 58 | [self addChild:self.waterNode]; 59 | 60 | // Settings Button 61 | UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; 62 | [button setTitle:@"Settings" forState:UIControlStateNormal]; 63 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 64 | button.translatesAutoresizingMaskIntoConstraints = NO; 65 | [button addTarget:self action:@selector(showSettingsView) forControlEvents:UIControlEventTouchUpInside]; 66 | [self.view addSubview:button]; 67 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 68 | attribute:NSLayoutAttributeRight 69 | relatedBy:NSLayoutRelationEqual 70 | toItem:self.view 71 | attribute:NSLayoutAttributeRight 72 | multiplier:1 73 | constant:-8]]; 74 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 75 | attribute:NSLayoutAttributeTop 76 | relatedBy:NSLayoutRelationEqual 77 | toItem:self.view 78 | attribute:NSLayoutAttributeTop 79 | multiplier:1 80 | constant:8]]; 81 | 82 | // Set Default Values 83 | [self setDefaultValues]; 84 | 85 | } 86 | 87 | -(void)setDefaultValues{ 88 | self.waterNode.surfaceHeight = kSurfaceHeight; 89 | self.splashWidth = 20; 90 | self.splashForceMultiplier = 0.125; 91 | [self.waterNode setDefaultValues]; 92 | } 93 | 94 | #pragma mark - Touch Handling 95 | 96 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 97 | 98 | for (UITouch *touch in touches) { 99 | CGPoint location = [touch locationInNode:self]; 100 | Rock *rock = [[Rock alloc]initWithImageNamed:@"rock"]; 101 | rock.position = location; 102 | rock.zPosition = ZPositionRock; 103 | [self addChild:rock]; 104 | [self.rocks addObject:rock]; 105 | } 106 | } 107 | 108 | #pragma mark - Update 109 | 110 | -(void)update:(CFTimeInterval)currentTime { 111 | /* Called before each frame is rendered */ 112 | 113 | if (!self.hasReferenceFrameTime) { 114 | self.lastFrameTime = currentTime; 115 | self.hasReferenceFrameTime = YES; 116 | return; 117 | } 118 | 119 | CFTimeInterval dt = currentTime - self.lastFrameTime; 120 | 121 | // Fixed Update 122 | CFTimeInterval accumilator = 0; 123 | accumilator += dt; 124 | 125 | while (accumilator >= kFixedTimeStep) { 126 | [self fixedUpdate:kFixedTimeStep]; 127 | accumilator -= kFixedTimeStep; 128 | } 129 | [self fixedUpdate:accumilator]; 130 | 131 | // Late Update 132 | [self lateUpdate:dt]; 133 | 134 | self.lastFrameTime = currentTime; 135 | 136 | } 137 | 138 | -(void)fixedUpdate:(CFTimeInterval)dt{ 139 | [self.waterNode update:dt]; 140 | 141 | 142 | NSMutableArray *rocksToRemove = [[NSMutableArray alloc]init]; 143 | 144 | const float gravity = -1200; 145 | for (Rock *rock in self.rocks) { 146 | 147 | // Apply Gravity 148 | rock.velocity = CGPointMake(rock.velocity.x, 149 | rock.velocity.y + gravity * dt); 150 | 151 | rock.position = CGPointMake(rock.position.x + rock.velocity.x * dt, 152 | rock.position.y + rock.velocity.y * dt); 153 | 154 | // Splash 155 | if (rock.isAboveWater && rock.position.y <= self.waterNode.surfaceHeight) { 156 | rock.isAboveWater = NO; 157 | [self.waterNode splashAtX:rock.position.x 158 | force:-rock.velocity.y * self.splashForceMultiplier 159 | width:self.splashWidth]; 160 | 161 | } 162 | 163 | // Remove if off-screen 164 | if (rock.position.y < - rock.size.height/2) { 165 | [rocksToRemove addObject:rock]; 166 | } 167 | } 168 | 169 | for (Rock *rock in rocksToRemove) { 170 | [self.rocks removeObject:rock]; 171 | } 172 | 173 | } 174 | 175 | -(void)lateUpdate:(CFTimeInterval)dt{ 176 | [self.waterNode render]; 177 | } 178 | 179 | #pragma mark - Settings View 180 | 181 | -(void)showSettingsView{ 182 | if (self.settingsView) { return; } 183 | 184 | self.settingsView = [SettingsView instanceFromNib]; 185 | self.settingsView.frame = self.view.bounds; 186 | self.settingsView.delegate = self; 187 | self.settingsView.gameScene = self; 188 | self.settingsView.waterNode = self.waterNode; 189 | [self.view addSubview:self.settingsView]; 190 | 191 | } 192 | 193 | -(void)settingsViewShouldClose:(SettingsView *)settingsView{ 194 | 195 | if (self.settingsView) { 196 | [self.settingsView removeFromSuperview]; 197 | self.settingsView = nil; 198 | } 199 | 200 | } 201 | 202 | -(void)settingsViewWantsRestoreDefaultValues:(SettingsView *)settingsView{ 203 | [self setDefaultValues]; 204 | } 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /Demo/DemoSource/SettingsView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsView.m 3 | // DynamicWater 4 | // 5 | // Created by Steve Barnegren on 09/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "SettingsView.h" 10 | #import "SettingsSliderCell.h" 11 | #import "SettingsTitleCell.h" 12 | #import "SettingsCellInfo.h" 13 | #import "GameScene.h" 14 | 15 | #define kCellHeight 45 16 | 17 | @interface SettingsView() 18 | @property (nonatomic, weak) IBOutlet UIView *containerView; 19 | @property (nonatomic, weak) IBOutlet UITableView *tableView; 20 | @property (nonatomic, weak) IBOutlet NSLayoutConstraint *tableViewHeightConstraint; 21 | @property (nonatomic, strong) NSArray *cellInfos; 22 | @end 23 | 24 | @implementation SettingsView 25 | 26 | +(id)instanceFromNib{ 27 | 28 | id result = nil; 29 | NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil]; 30 | for (id anObject in elements) 31 | { 32 | if ([anObject isKindOfClass:[self class]]) 33 | { 34 | result = anObject; 35 | break; 36 | } 37 | } 38 | NSAssert1(result, @"Failed to load %@ from nib", NSStringFromClass(self)); 39 | 40 | return result; 41 | } 42 | 43 | -(void)awakeFromNib{ 44 | [super awakeFromNib]; 45 | 46 | self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3]; 47 | [self createCellInfos]; 48 | 49 | // Container View 50 | self.containerView.layer.cornerRadius = 15; 51 | self.containerView.clipsToBounds = YES; 52 | 53 | // TableView 54 | self.tableViewHeightConstraint.constant = kCellHeight * self.cellInfos.count; 55 | self.tableView.dataSource = self; 56 | self.tableView.delegate = self; 57 | [self.tableView registerNib:[UINib nibWithNibName:@"SettingsSliderCell" bundle:nil] 58 | forCellReuseIdentifier:NSStringFromClass([SettingsSliderCell class])]; 59 | [self.tableView registerNib:[UINib nibWithNibName:@"SettingsTitleCell" bundle:nil] 60 | forCellReuseIdentifier:NSStringFromClass([SettingsTitleCell class])]; 61 | [self.tableView reloadData]; 62 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 63 | 64 | 65 | } 66 | 67 | -(void)createCellInfos{ 68 | 69 | NSMutableArray *cellInfos = [[NSMutableArray alloc]init]; 70 | 71 | __weak __typeof__(self) weakSelf = self; 72 | 73 | [cellInfos addObject:[SettingsCellInfo cellInfoWithTitle:@"Water"]]; 74 | 75 | // Surface Height 76 | { 77 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 78 | info.title = @"Surface Height"; 79 | info.startValue = 150; 80 | info.endValue = 500; 81 | info.showValueReadout = YES; 82 | [info setGetCurrentValue:^float{ 83 | return weakSelf.waterNode.surfaceHeight; 84 | }]; 85 | [info setUpdateCallback:^void(float newValue) { 86 | weakSelf.waterNode.surfaceHeight = newValue; 87 | }]; 88 | [cellInfos addObject:info]; 89 | } 90 | 91 | // Tension 92 | { 93 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 94 | info.title = @"Tension"; 95 | info.startValue = 0.05; 96 | info.endValue = 15; 97 | info.showValueReadout = YES; 98 | [info setGetCurrentValue:^float{ 99 | return weakSelf.waterNode.tension; 100 | }]; 101 | [info setUpdateCallback:^void(float newValue) { 102 | weakSelf.waterNode.tension = newValue; 103 | }]; 104 | [cellInfos addObject:info]; 105 | } 106 | 107 | // Damping 108 | { 109 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 110 | info.title = @"Damping"; 111 | info.startValue = 0.15; 112 | info.endValue = 15; 113 | info.showValueReadout = YES; 114 | [info setGetCurrentValue:^float{ 115 | return weakSelf.waterNode.damping; 116 | }]; 117 | [info setUpdateCallback:^void(float newValue) { 118 | weakSelf.waterNode.damping = newValue; 119 | }]; 120 | [cellInfos addObject:info]; 121 | } 122 | 123 | // Spread 124 | { 125 | 126 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 127 | info.title = @"Spread"; 128 | info.startValue = 3; 129 | info.endValue = 100; 130 | info.showValueReadout = YES; 131 | [info setGetCurrentValue:^float{ 132 | return weakSelf.waterNode.spread; 133 | }]; 134 | [info setUpdateCallback:^void(float newValue) { 135 | weakSelf.waterNode.spread = newValue; 136 | }]; 137 | [cellInfos addObject:info]; 138 | } 139 | 140 | [cellInfos addObject:[SettingsCellInfo cellInfoWithTitle:@"Splashes"]]; 141 | 142 | // Splash Force Multiplier 143 | { 144 | 145 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 146 | info.title = @"Splash Force"; 147 | info.startValue = 0.02; 148 | info.endValue = 2.5; 149 | info.showValueReadout = NO; 150 | [info setGetCurrentValue:^float{ 151 | return weakSelf.gameScene.splashForceMultiplier; 152 | }]; 153 | [info setUpdateCallback:^void(float newValue) { 154 | weakSelf.gameScene.splashForceMultiplier = newValue; 155 | }]; 156 | [cellInfos addObject:info]; 157 | } 158 | 159 | // Splash Width 160 | { 161 | 162 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 163 | info.title = @"Splash Width"; 164 | info.startValue = 0; 165 | info.endValue = 200; 166 | info.showValueReadout = YES; 167 | [info setGetCurrentValue:^float{ 168 | return weakSelf.gameScene.splashWidth; 169 | }]; 170 | [info setUpdateCallback:^void(float newValue) { 171 | weakSelf.gameScene.splashWidth = newValue; 172 | }]; 173 | [cellInfos addObject:info]; 174 | } 175 | 176 | [cellInfos addObject:[SettingsCellInfo cellInfoWithTitle:@"Droplets"]]; 177 | 178 | // Droplets Force 179 | { 180 | 181 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 182 | info.title = @"Droplets Force"; 183 | info.startValue = 0.2; 184 | info.endValue = 3; 185 | info.showValueReadout = YES; 186 | [info setGetCurrentValue:^float{ 187 | return weakSelf.waterNode.dropletsForce; 188 | }]; 189 | [info setUpdateCallback:^void(float newValue) { 190 | weakSelf.waterNode.dropletsForce = newValue; 191 | }]; 192 | [cellInfos addObject:info]; 193 | } 194 | 195 | // Droplets Density 196 | { 197 | 198 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 199 | info.title = @"Droplets Density"; 200 | info.startValue = 0; 201 | info.endValue = 2; 202 | info.showValueReadout = YES; 203 | [info setGetCurrentValue:^float{ 204 | return weakSelf.waterNode.dropletsDensity; 205 | }]; 206 | [info setUpdateCallback:^void(float newValue) { 207 | weakSelf.waterNode.dropletsDensity = newValue; 208 | }]; 209 | [cellInfos addObject:info]; 210 | } 211 | 212 | // Droplet Size 213 | { 214 | 215 | SettingsCellSliderInfo *info = [[SettingsCellSliderInfo alloc]init]; 216 | info.title = @"Droplet Size"; 217 | info.startValue = 1; 218 | info.endValue = 10; 219 | info.showValueReadout = YES; 220 | [info setGetCurrentValue:^float{ 221 | return weakSelf.waterNode.dropletSize; 222 | }]; 223 | [info setUpdateCallback:^void(float newValue) { 224 | weakSelf.waterNode.dropletSize = newValue; 225 | }]; 226 | [cellInfos addObject:info]; 227 | } 228 | 229 | self.cellInfos = [NSArray arrayWithArray:cellInfos]; 230 | } 231 | 232 | #pragma mark - Actions 233 | 234 | -(IBAction)restoreDefaultsButtonPressed:(id)sender{ 235 | 236 | if ([self.delegate respondsToSelector:@selector(settingsViewWantsRestoreDefaultValues:)]) { 237 | [self.delegate settingsViewWantsRestoreDefaultValues:self]; 238 | } 239 | [self.tableView reloadData]; 240 | } 241 | 242 | -(IBAction)closeButtonPressed:(id)sender{ 243 | 244 | if ([self.delegate respondsToSelector:@selector(settingsViewShouldClose:)]) { 245 | [self.delegate settingsViewShouldClose:self]; 246 | } 247 | } 248 | 249 | #pragma mark - UITableView Datasource / Delegate 250 | 251 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 252 | return 1; 253 | } 254 | 255 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 256 | return self.cellInfos.count; 257 | } 258 | 259 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 260 | 261 | SettingsCellInfo *info = self.cellInfos[indexPath.row]; 262 | 263 | if ([info isMemberOfClass:[SettingsCellInfo class]]) { 264 | SettingsTitleCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SettingsTitleCell class])]; 265 | cell.title = info.title; 266 | return cell; 267 | } 268 | 269 | if ([info isMemberOfClass:[SettingsCellSliderInfo class]]) { 270 | SettingsSliderCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SettingsSliderCell class])]; 271 | [cell setupWithInfo:self.cellInfos[indexPath.row]]; 272 | return cell; 273 | } 274 | 275 | return nil; 276 | } 277 | 278 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 279 | return kCellHeight; 280 | } 281 | 282 | @end 283 | -------------------------------------------------------------------------------- /SBDynamicWaterNode/SBDynamicWaterNode.m: -------------------------------------------------------------------------------- 1 | // 2 | // SBDynamicWaterNode.m 3 | // SBDynamicWaterNode 4 | // 5 | // Created by Steve Barnegren on 07/03/2016. 6 | // Copyright © 2016 Steve Barnegren. All rights reserved. 7 | // 8 | 9 | #import "SBDynamicWaterNode.h" 10 | 11 | @interface UIColor (SBDynamicWaterNodeExtensions) 12 | -(GLKVector4)vector4Value; 13 | @end 14 | 15 | @implementation UIColor (SBDynamicWaterNodeExtensions) 16 | 17 | -(GLKVector4)vector4Value{ 18 | 19 | CGFloat r, g, b, a; 20 | [self getRed:&r green:&g blue:&b alpha:&a]; 21 | return GLKVector4Make(r, g, b, a); 22 | } 23 | 24 | @end 25 | 26 | //********************************************** 27 | #pragma mark - ***** Droplet ***** 28 | //********************************************** 29 | 30 | @interface SBDroplet : SKSpriteNode 31 | @property CGPoint velocity; 32 | @end 33 | 34 | @implementation SBDroplet 35 | 36 | +(instancetype)droplet{ 37 | SBDroplet *droplet = [[SBDroplet alloc]initWithImageNamed:@"Droplet"]; 38 | droplet.velocity = CGPointZero; 39 | return droplet; 40 | } 41 | 42 | @end 43 | 44 | //********************************************** 45 | #pragma mark - ***** WaterJoint ***** 46 | //********************************************** 47 | 48 | @interface SBWaterJoint : NSObject 49 | 50 | @property (nonatomic) CGPoint position; 51 | @property (nonatomic) CGFloat velocity; 52 | @property (nonatomic) CGFloat damping; 53 | @property (nonatomic) CGFloat tension; 54 | @end 55 | 56 | @implementation SBWaterJoint 57 | 58 | -(instancetype)init{ 59 | 60 | if (self = [super init]) { 61 | self.position = CGPointZero; 62 | self.velocity = 0; 63 | self.damping = 0; 64 | self.tension = 0; 65 | } 66 | return self; 67 | } 68 | 69 | -(void)setYPosition:(float)yPos{ 70 | self.position = CGPointMake(self.position.x, yPos); 71 | } 72 | 73 | - (void)update:(NSTimeInterval)dt { 74 | 75 | CGFloat y = self.position.y; 76 | CGFloat acceleration = (-self.tension * y) - (self.velocity * self.damping); 77 | 78 | self.position = CGPointMake(self.position.x, self.position.y + (self.velocity * 60 * dt)); 79 | self.velocity += acceleration * dt; 80 | } 81 | 82 | @end 83 | 84 | //********************************************** 85 | #pragma mark - ***** DynamicWaterNode ***** 86 | //********************************************** 87 | 88 | @interface SBDynamicWaterNode () 89 | @property (nonatomic, strong) NSArray *joints; 90 | @property (nonatomic, strong) SKShapeNode *shapeNode; 91 | @property float width; 92 | 93 | @property CGPathRef path; 94 | 95 | @property (nonatomic, strong) NSMutableArray *droplets; 96 | @property (nonatomic, strong) NSMutableArray *dropletsCache; 97 | 98 | @property (nonatomic, strong) SKEffectNode *effectNode; 99 | 100 | @end 101 | 102 | 103 | @implementation SBDynamicWaterNode 104 | 105 | #pragma mark - LifeCycle 106 | 107 | -(instancetype)initWithWidth:(float)width numJoints:(NSInteger)numJoints surfaceHeight:(float)surfaceHeight fillColour:(UIColor*)fillColour{ 108 | 109 | self = [super init]; 110 | if (!self) { return nil; } 111 | 112 | // Init Properties 113 | self.surfaceHeight = surfaceHeight; 114 | self.width = width; 115 | self.droplets = [[NSMutableArray alloc]init]; 116 | self.dropletsCache = [[NSMutableArray alloc]init]; 117 | 118 | // Effect Node 119 | self.effectNode = [[SKEffectNode alloc]init]; 120 | self.effectNode.position = CGPointZero; 121 | self.effectNode.zPosition = 1; 122 | self.effectNode.shouldRasterize = NO; 123 | self.effectNode.shouldEnableEffects = YES; 124 | self.effectNode.shader = [SKShader shaderWithFileNamed:@"Droplets.fsh"]; 125 | self.effectNode.shader.uniforms = @[[SKUniform uniformWithName:@"u_colour" floatVector4:[fillColour vector4Value]]]; 126 | [self addChild:self.effectNode]; 127 | 128 | // Shape Node 129 | self.shapeNode = [[SKShapeNode alloc]init]; 130 | self.shapeNode.fillColor = [UIColor blackColor]; 131 | self.shapeNode.strokeColor = [UIColor greenColor]; 132 | self.shapeNode.glowWidth = 2; 133 | self.shapeNode.zPosition = 2; 134 | [self.effectNode addChild:self.shapeNode]; 135 | 136 | // Create joints 137 | NSMutableArray *mutableJoints = [[NSMutableArray alloc]initWithCapacity:numJoints]; 138 | for (NSInteger i = 0; i < numJoints; i++) { 139 | SBWaterJoint *joint = [[SBWaterJoint alloc]init]; 140 | CGPoint position; 141 | position.x = -(width/2) + ((width/(numJoints-1)) * i); 142 | position.y = 0; 143 | joint.position = position; 144 | [mutableJoints addObject:joint]; 145 | } 146 | self.joints = [NSArray arrayWithArray:mutableJoints]; 147 | 148 | // Set default simulation variables 149 | [self setDefaultValues]; 150 | 151 | // Initial render 152 | [self render]; 153 | 154 | return self; 155 | } 156 | 157 | -(void)dealloc{ 158 | CGPathRelease(self.path); 159 | } 160 | 161 | #pragma mark - Simulation Variables 162 | 163 | -(void)setDefaultValues{ 164 | self.tension = 1.8; 165 | self.damping = 2.4; 166 | self.spread = 9; 167 | self.dropletsForce = 1; 168 | self.dropletsDensity = 1; 169 | self.dropletSize = 3; 170 | } 171 | 172 | -(void)setTension:(float)tension{ 173 | _tension = tension; 174 | for (SBWaterJoint *joint in self.joints) { 175 | joint.tension = tension; 176 | } 177 | } 178 | 179 | -(void)setDamping:(float)damping{ 180 | _damping = damping; 181 | for (SBWaterJoint *joint in self.joints) { 182 | joint.damping = damping; 183 | } 184 | } 185 | 186 | -(void)setColour:(UIColor*)colour{ 187 | [self.effectNode.shader uniformNamed:@"u_colour"].floatVector4Value = [colour vector4Value]; 188 | } 189 | 190 | #pragma mark - Splash 191 | 192 | -(void)splashAtX:(float)xLocation force:(CGFloat)force{ 193 | [self splashAtX:xLocation force:force width:0]; 194 | } 195 | 196 | -(void)splashAtX:(float)xLocation force:(CGFloat)force width:(float)width{ 197 | 198 | xLocation -= self.width/2; 199 | 200 | CGFloat shortestDistance = CGFLOAT_MAX; 201 | SBWaterJoint *closestJoint; 202 | 203 | for (SBWaterJoint *joint in self.joints) { 204 | 205 | CGFloat distance = fabs(joint.position.x - xLocation); 206 | if (distance < shortestDistance) { 207 | shortestDistance = distance; 208 | closestJoint = joint; 209 | } 210 | } 211 | 212 | closestJoint.velocity = -force; 213 | 214 | for (SBWaterJoint *joint in self.joints) { 215 | CGFloat distance = fabs(joint.position.x - closestJoint.position.x); 216 | if (distance < width) { 217 | joint.velocity = distance / width * -force; 218 | } 219 | } 220 | 221 | 222 | // Add droplets 223 | NSInteger numDroplets = 20 * force/100 * self.dropletsDensity; 224 | //NSLog(@"Num Droplets: %li", (long)numDroplets); 225 | for (NSInteger i = 0; i < numDroplets; i++) { 226 | const float maxVelY = 500 * force/100*self.dropletsForce; 227 | const float minVelY = 200 * force/100*self.dropletsForce; 228 | const float maxVelX = -350 * force/100*self.dropletsForce; 229 | const float minVelX = 350 * force/100*self.dropletsForce; 230 | 231 | float velY = minVelY + (maxVelY - minVelY) * [self randomFloatBetween0and1]; 232 | float velX = minVelX + (maxVelX - minVelX) * [self randomFloatBetween0and1]; 233 | 234 | [self addDropletAt:CGPointMake(xLocation, self.surfaceHeight) 235 | velocity:CGPointMake(velX, velY)]; 236 | } 237 | 238 | } 239 | 240 | -(float)randomFloatBetween0and1{ 241 | return (float)rand() / RAND_MAX; 242 | } 243 | 244 | #pragma mark - Droplets 245 | 246 | -(void)addDropletAt:(CGPoint)position velocity:(CGPoint)velocity{ 247 | 248 | SBDroplet *droplet; 249 | 250 | if (self.dropletsCache.count) { 251 | droplet = [self.dropletsCache lastObject]; 252 | [self.dropletsCache removeLastObject]; 253 | } 254 | else{ 255 | droplet = [SBDroplet droplet]; 256 | } 257 | 258 | droplet.velocity = velocity; 259 | droplet.position = position; 260 | droplet.zPosition = 1; 261 | droplet.blendMode = SKBlendModeAlpha; 262 | droplet.color = [UIColor blueColor]; 263 | droplet.colorBlendFactor = 1; 264 | droplet.xScale = droplet.yScale = self.dropletSize; 265 | [self.effectNode addChild:droplet]; 266 | [self.droplets addObject:droplet]; 267 | } 268 | 269 | -(void)removeDroplet:(SBDroplet*)droplet{ 270 | 271 | [droplet removeFromParent]; 272 | [self.droplets removeObject:droplet]; 273 | [self.dropletsCache addObject:droplet]; 274 | } 275 | 276 | 277 | #pragma mark - Update 278 | 279 | -(void)update:(CFTimeInterval)dt{ 280 | [self updateJoints:dt]; 281 | [self updateDroplets:dt]; 282 | } 283 | 284 | -(void)updateJoints:(CFTimeInterval)dt{ 285 | 286 | 287 | for (SBWaterJoint *joint in self.joints) { 288 | [joint update:dt]; 289 | } 290 | 291 | float leftDeltas[self.joints.count]; 292 | float rightDeltas[self.joints.count]; 293 | 294 | for (NSInteger pass = 0; pass < 1; pass++) { 295 | 296 | for (NSInteger i = 0; i < self.joints.count; i++) { 297 | 298 | SBWaterJoint *currentJoint = self.joints[i]; 299 | 300 | if (i > 0) { 301 | SBWaterJoint *previousJoint = self.joints[i-1]; 302 | leftDeltas[i] = self.spread * (currentJoint.position.y - previousJoint.position.y); 303 | previousJoint.velocity += leftDeltas[i] * dt; 304 | } 305 | if (i < self.joints.count-1) { 306 | SBWaterJoint *nextJoint = self.joints[i+1]; 307 | rightDeltas[i] = self.spread * (currentJoint.position.y - nextJoint.position.y); 308 | nextJoint.velocity += rightDeltas[i] * dt; 309 | } 310 | } 311 | 312 | for (NSInteger i = 0; i < self.joints.count; i++) { 313 | 314 | if (i > 0) { 315 | SBWaterJoint *previousJoint = self.joints[i-1]; 316 | [previousJoint setYPosition:previousJoint.position.y + leftDeltas[i] * dt]; 317 | } 318 | if (i < self.joints.count - 1) { 319 | SBWaterJoint *nextJoint = self.joints[i+1]; 320 | [nextJoint setYPosition:nextJoint.position.y + rightDeltas[i] * dt]; 321 | } 322 | 323 | } 324 | 325 | } 326 | } 327 | 328 | -(void)updateDroplets:(CFTimeInterval)dt{ 329 | 330 | const float gravity = -1200; 331 | 332 | NSMutableArray *dropletsToRemove = [[NSMutableArray alloc]init]; 333 | 334 | for (SBDroplet *droplet in self.droplets) { 335 | 336 | // Apply Gravity 337 | droplet.velocity = CGPointMake(droplet.velocity.x, 338 | droplet.velocity.y + gravity * dt); 339 | 340 | droplet.position = CGPointMake(droplet.position.x + droplet.velocity.x * dt, 341 | droplet.position.y + droplet.velocity.y * dt); 342 | 343 | // Remove if below surface 344 | if (droplet.position.y + droplet.texture.size.height/2 + 30 < self.surfaceHeight) { 345 | [dropletsToRemove addObject:droplet]; 346 | } 347 | } 348 | 349 | for (SBDroplet *droplet in dropletsToRemove) { 350 | [self removeDroplet:droplet]; 351 | } 352 | 353 | } 354 | 355 | #pragma mark - Render 356 | 357 | -(void)render{ 358 | 359 | CGPathRelease(self.path); 360 | self.path = [self pathFromJoints:self.joints]; 361 | 362 | [self.shapeNode setPath:self.path]; 363 | } 364 | 365 | - (CGPathRef)pathFromJoints:(NSArray*)joints { 366 | 367 | CGMutablePathRef path = CGPathCreateMutable(); 368 | 369 | NSInteger index = 0; 370 | for (SBWaterJoint *joint in self.joints) { 371 | 372 | if (index == 0) { 373 | CGPathMoveToPoint(path, 374 | nil, 375 | joint.position.x, 376 | joint.position.y + self.surfaceHeight); 377 | } 378 | else{ 379 | CGPathAddLineToPoint(path, 380 | nil, 381 | joint.position.x, 382 | joint.position.y + self.surfaceHeight); 383 | } 384 | 385 | index++; 386 | } 387 | 388 | // Bottom Right 389 | CGPathAddLineToPoint(path, 390 | nil, 391 | self.width/2, 392 | 0); 393 | 394 | // Bottom Left 395 | CGPathAddLineToPoint(path, 396 | nil, 397 | -self.width/2, 398 | 0); 399 | 400 | CGPathCloseSubpath(path); 401 | 402 | 403 | return path; 404 | } 405 | 406 | 407 | @end 408 | -------------------------------------------------------------------------------- /Demo/DynamicWater.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 67AC79761CBEE56700551E1F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19521C8DE6C500BB9382 /* Info.plist */; }; 11 | 67DA193E1C8DE6C500BB9382 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA193D1C8DE6C500BB9382 /* main.m */; }; 12 | 67DA19411C8DE6C500BB9382 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19401C8DE6C500BB9382 /* AppDelegate.m */; }; 13 | 67DA19441C8DE6C500BB9382 /* GameScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19431C8DE6C500BB9382 /* GameScene.m */; }; 14 | 67DA19461C8DE6C500BB9382 /* GameScene.sks in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19451C8DE6C500BB9382 /* GameScene.sks */; }; 15 | 67DA19491C8DE6C500BB9382 /* GameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19481C8DE6C500BB9382 /* GameViewController.m */; }; 16 | 67DA194C1C8DE6C500BB9382 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67DA194A1C8DE6C500BB9382 /* Main.storyboard */; }; 17 | 67DA194E1C8DE6C500BB9382 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67DA194D1C8DE6C500BB9382 /* Assets.xcassets */; }; 18 | 67DA19511C8DE6C500BB9382 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67DA194F1C8DE6C500BB9382 /* LaunchScreen.storyboard */; }; 19 | 67DA195A1C8DE6F400BB9382 /* SBDynamicWaterNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19591C8DE6F400BB9382 /* SBDynamicWaterNode.m */; }; 20 | 67DA195F1C8E542300BB9382 /* rock.png in Resources */ = {isa = PBXBuildFile; fileRef = 67DA195C1C8E542300BB9382 /* rock.png */; }; 21 | 67DA19601C8E542300BB9382 /* sky.png in Resources */ = {isa = PBXBuildFile; fileRef = 67DA195D1C8E542300BB9382 /* sky.png */; }; 22 | 67DA19B31C8F39A400BB9382 /* Rock.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19B21C8F39A400BB9382 /* Rock.m */; }; 23 | 67DA19BA1C8F8B7900BB9382 /* Droplets.fsh in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19B91C8F8B7900BB9382 /* Droplets.fsh */; }; 24 | 67DA19C71C90794800BB9382 /* SettingsSliderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19C51C90794800BB9382 /* SettingsSliderCell.m */; }; 25 | 67DA19C81C90794800BB9382 /* SettingsSliderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19C61C90794800BB9382 /* SettingsSliderCell.xib */; }; 26 | 67DA19CB1C90809D00BB9382 /* SettingsCellInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19CA1C90809D00BB9382 /* SettingsCellInfo.m */; }; 27 | 67DA19CF1C909FEF00BB9382 /* SettingsView.h in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19B41C8F7E2D00BB9382 /* SettingsView.h */; }; 28 | 67DA19D01C909FEF00BB9382 /* SettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19B51C8F7E2D00BB9382 /* SettingsView.m */; }; 29 | 67DA19D21C90A06A00BB9382 /* Droplet.png in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19D11C90A06A00BB9382 /* Droplet.png */; }; 30 | 67DA19D31C90A2C300BB9382 /* SettingsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19B71C8F7E4500BB9382 /* SettingsView.xib */; }; 31 | 67DA19D71C90C0EC00BB9382 /* SettingsTitleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 67DA19D51C90C0EC00BB9382 /* SettingsTitleCell.m */; }; 32 | 67DA19D81C90C0EC00BB9382 /* SettingsTitleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67DA19D61C90C0EC00BB9382 /* SettingsTitleCell.xib */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 67DA19391C8DE6C400BB9382 /* DynamicWater.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DynamicWater.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 67DA193D1C8DE6C500BB9382 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | 67DA193F1C8DE6C500BB9382 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | 67DA19401C8DE6C500BB9382 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | 67DA19421C8DE6C500BB9382 /* GameScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GameScene.h; sourceTree = ""; }; 41 | 67DA19431C8DE6C500BB9382 /* GameScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GameScene.m; sourceTree = ""; }; 42 | 67DA19451C8DE6C500BB9382 /* GameScene.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = GameScene.sks; sourceTree = ""; }; 43 | 67DA19471C8DE6C500BB9382 /* GameViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GameViewController.h; sourceTree = ""; }; 44 | 67DA19481C8DE6C500BB9382 /* GameViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GameViewController.m; sourceTree = ""; }; 45 | 67DA194B1C8DE6C500BB9382 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 67DA194D1C8DE6C500BB9382 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 67DA19501C8DE6C500BB9382 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 67DA19521C8DE6C500BB9382 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 67DA19581C8DE6F400BB9382 /* SBDynamicWaterNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBDynamicWaterNode.h; sourceTree = ""; }; 50 | 67DA19591C8DE6F400BB9382 /* SBDynamicWaterNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBDynamicWaterNode.m; sourceTree = ""; }; 51 | 67DA195C1C8E542300BB9382 /* rock.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rock.png; sourceTree = ""; }; 52 | 67DA195D1C8E542300BB9382 /* sky.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sky.png; sourceTree = ""; }; 53 | 67DA19B11C8F39A400BB9382 /* Rock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Rock.h; sourceTree = ""; }; 54 | 67DA19B21C8F39A400BB9382 /* Rock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Rock.m; sourceTree = ""; }; 55 | 67DA19B41C8F7E2D00BB9382 /* SettingsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsView.h; sourceTree = ""; }; 56 | 67DA19B51C8F7E2D00BB9382 /* SettingsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsView.m; sourceTree = ""; }; 57 | 67DA19B71C8F7E4500BB9382 /* SettingsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsView.xib; sourceTree = ""; }; 58 | 67DA19B91C8F8B7900BB9382 /* Droplets.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Droplets.fsh; sourceTree = ""; }; 59 | 67DA19C41C90794800BB9382 /* SettingsSliderCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsSliderCell.h; sourceTree = ""; }; 60 | 67DA19C51C90794800BB9382 /* SettingsSliderCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsSliderCell.m; sourceTree = ""; }; 61 | 67DA19C61C90794800BB9382 /* SettingsSliderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsSliderCell.xib; sourceTree = ""; }; 62 | 67DA19C91C90809D00BB9382 /* SettingsCellInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsCellInfo.h; sourceTree = ""; }; 63 | 67DA19CA1C90809D00BB9382 /* SettingsCellInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsCellInfo.m; sourceTree = ""; }; 64 | 67DA19D11C90A06A00BB9382 /* Droplet.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Droplet.png; sourceTree = ""; }; 65 | 67DA19D41C90C0EC00BB9382 /* SettingsTitleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsTitleCell.h; sourceTree = ""; }; 66 | 67DA19D51C90C0EC00BB9382 /* SettingsTitleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsTitleCell.m; sourceTree = ""; }; 67 | 67DA19D61C90C0EC00BB9382 /* SettingsTitleCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTitleCell.xib; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 67DA19361C8DE6C400BB9382 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | /* End PBXFrameworksBuildPhase section */ 79 | 80 | /* Begin PBXGroup section */ 81 | 67DA19301C8DE6C400BB9382 = { 82 | isa = PBXGroup; 83 | children = ( 84 | 67DA19CC1C90950500BB9382 /* SBDynamicWaterNode */, 85 | 67DA193B1C8DE6C500BB9382 /* Demo */, 86 | 67DA193A1C8DE6C400BB9382 /* Products */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 67DA193A1C8DE6C400BB9382 /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 67DA19391C8DE6C400BB9382 /* DynamicWater.app */, 94 | ); 95 | name = Products; 96 | sourceTree = ""; 97 | }; 98 | 67DA193B1C8DE6C500BB9382 /* Demo */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 67DA195C1C8E542300BB9382 /* rock.png */, 102 | 67DA195D1C8E542300BB9382 /* sky.png */, 103 | 67DA193F1C8DE6C500BB9382 /* AppDelegate.h */, 104 | 67DA19401C8DE6C500BB9382 /* AppDelegate.m */, 105 | 67DA19421C8DE6C500BB9382 /* GameScene.h */, 106 | 67DA19431C8DE6C500BB9382 /* GameScene.m */, 107 | 67DA19B11C8F39A400BB9382 /* Rock.h */, 108 | 67DA19B21C8F39A400BB9382 /* Rock.m */, 109 | 67DA19451C8DE6C500BB9382 /* GameScene.sks */, 110 | 67DA19471C8DE6C500BB9382 /* GameViewController.h */, 111 | 67DA19481C8DE6C500BB9382 /* GameViewController.m */, 112 | 67DA194A1C8DE6C500BB9382 /* Main.storyboard */, 113 | 67DA19D91C90C0F200BB9382 /* Settings */, 114 | 67DA194D1C8DE6C500BB9382 /* Assets.xcassets */, 115 | 67DA194F1C8DE6C500BB9382 /* LaunchScreen.storyboard */, 116 | 67DA19521C8DE6C500BB9382 /* Info.plist */, 117 | 67DA193C1C8DE6C500BB9382 /* Supporting Files */, 118 | ); 119 | name = Demo; 120 | path = DemoSource; 121 | sourceTree = ""; 122 | }; 123 | 67DA193C1C8DE6C500BB9382 /* Supporting Files */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 67DA193D1C8DE6C500BB9382 /* main.m */, 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | 67DA19CC1C90950500BB9382 /* SBDynamicWaterNode */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 67DA19D11C90A06A00BB9382 /* Droplet.png */, 135 | 67DA19581C8DE6F400BB9382 /* SBDynamicWaterNode.h */, 136 | 67DA19591C8DE6F400BB9382 /* SBDynamicWaterNode.m */, 137 | 67DA19B91C8F8B7900BB9382 /* Droplets.fsh */, 138 | ); 139 | name = SBDynamicWaterNode; 140 | path = ../SBDynamicWaterNode; 141 | sourceTree = ""; 142 | }; 143 | 67DA19D91C90C0F200BB9382 /* Settings */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 67DA19B41C8F7E2D00BB9382 /* SettingsView.h */, 147 | 67DA19B51C8F7E2D00BB9382 /* SettingsView.m */, 148 | 67DA19B71C8F7E4500BB9382 /* SettingsView.xib */, 149 | 67DA19C91C90809D00BB9382 /* SettingsCellInfo.h */, 150 | 67DA19CA1C90809D00BB9382 /* SettingsCellInfo.m */, 151 | 67DA19C41C90794800BB9382 /* SettingsSliderCell.h */, 152 | 67DA19C51C90794800BB9382 /* SettingsSliderCell.m */, 153 | 67DA19C61C90794800BB9382 /* SettingsSliderCell.xib */, 154 | 67DA19D41C90C0EC00BB9382 /* SettingsTitleCell.h */, 155 | 67DA19D51C90C0EC00BB9382 /* SettingsTitleCell.m */, 156 | 67DA19D61C90C0EC00BB9382 /* SettingsTitleCell.xib */, 157 | ); 158 | name = Settings; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | 67DA19381C8DE6C400BB9382 /* DynamicWater */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = 67DA19551C8DE6C500BB9382 /* Build configuration list for PBXNativeTarget "DynamicWater" */; 167 | buildPhases = ( 168 | 67DA19351C8DE6C400BB9382 /* Sources */, 169 | 67DA19361C8DE6C400BB9382 /* Frameworks */, 170 | 67DA19371C8DE6C400BB9382 /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = DynamicWater; 177 | productName = DynamicWater; 178 | productReference = 67DA19391C8DE6C400BB9382 /* DynamicWater.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | /* End PBXNativeTarget section */ 182 | 183 | /* Begin PBXProject section */ 184 | 67DA19311C8DE6C400BB9382 /* Project object */ = { 185 | isa = PBXProject; 186 | attributes = { 187 | LastUpgradeCheck = 0720; 188 | ORGANIZATIONNAME = "Steve Barnegren"; 189 | TargetAttributes = { 190 | 67DA19381C8DE6C400BB9382 = { 191 | CreatedOnToolsVersion = 7.2; 192 | }; 193 | }; 194 | }; 195 | buildConfigurationList = 67DA19341C8DE6C400BB9382 /* Build configuration list for PBXProject "DynamicWater" */; 196 | compatibilityVersion = "Xcode 3.2"; 197 | developmentRegion = English; 198 | hasScannedForEncodings = 0; 199 | knownRegions = ( 200 | en, 201 | Base, 202 | ); 203 | mainGroup = 67DA19301C8DE6C400BB9382; 204 | productRefGroup = 67DA193A1C8DE6C400BB9382 /* Products */; 205 | projectDirPath = ""; 206 | projectRoot = ""; 207 | targets = ( 208 | 67DA19381C8DE6C400BB9382 /* DynamicWater */, 209 | ); 210 | }; 211 | /* End PBXProject section */ 212 | 213 | /* Begin PBXResourcesBuildPhase section */ 214 | 67DA19371C8DE6C400BB9382 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 67DA19D21C90A06A00BB9382 /* Droplet.png in Resources */, 219 | 67DA19461C8DE6C500BB9382 /* GameScene.sks in Resources */, 220 | 67DA19D81C90C0EC00BB9382 /* SettingsTitleCell.xib in Resources */, 221 | 67DA19511C8DE6C500BB9382 /* LaunchScreen.storyboard in Resources */, 222 | 67AC79761CBEE56700551E1F /* Info.plist in Resources */, 223 | 67DA195F1C8E542300BB9382 /* rock.png in Resources */, 224 | 67DA19C81C90794800BB9382 /* SettingsSliderCell.xib in Resources */, 225 | 67DA194E1C8DE6C500BB9382 /* Assets.xcassets in Resources */, 226 | 67DA19601C8E542300BB9382 /* sky.png in Resources */, 227 | 67DA19D31C90A2C300BB9382 /* SettingsView.xib in Resources */, 228 | 67DA19BA1C8F8B7900BB9382 /* Droplets.fsh in Resources */, 229 | 67DA194C1C8DE6C500BB9382 /* Main.storyboard in Resources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXResourcesBuildPhase section */ 234 | 235 | /* Begin PBXSourcesBuildPhase section */ 236 | 67DA19351C8DE6C400BB9382 /* Sources */ = { 237 | isa = PBXSourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | 67DA19D71C90C0EC00BB9382 /* SettingsTitleCell.m in Sources */, 241 | 67DA19CF1C909FEF00BB9382 /* SettingsView.h in Sources */, 242 | 67DA19D01C909FEF00BB9382 /* SettingsView.m in Sources */, 243 | 67DA19491C8DE6C500BB9382 /* GameViewController.m in Sources */, 244 | 67DA19CB1C90809D00BB9382 /* SettingsCellInfo.m in Sources */, 245 | 67DA19411C8DE6C500BB9382 /* AppDelegate.m in Sources */, 246 | 67DA19B31C8F39A400BB9382 /* Rock.m in Sources */, 247 | 67DA19C71C90794800BB9382 /* SettingsSliderCell.m in Sources */, 248 | 67DA19441C8DE6C500BB9382 /* GameScene.m in Sources */, 249 | 67DA195A1C8DE6F400BB9382 /* SBDynamicWaterNode.m in Sources */, 250 | 67DA193E1C8DE6C500BB9382 /* main.m in Sources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | /* End PBXSourcesBuildPhase section */ 255 | 256 | /* Begin PBXVariantGroup section */ 257 | 67DA194A1C8DE6C500BB9382 /* Main.storyboard */ = { 258 | isa = PBXVariantGroup; 259 | children = ( 260 | 67DA194B1C8DE6C500BB9382 /* Base */, 261 | ); 262 | name = Main.storyboard; 263 | sourceTree = ""; 264 | }; 265 | 67DA194F1C8DE6C500BB9382 /* LaunchScreen.storyboard */ = { 266 | isa = PBXVariantGroup; 267 | children = ( 268 | 67DA19501C8DE6C500BB9382 /* Base */, 269 | ); 270 | name = LaunchScreen.storyboard; 271 | sourceTree = ""; 272 | }; 273 | /* End PBXVariantGroup section */ 274 | 275 | /* Begin XCBuildConfiguration section */ 276 | 67DA19531C8DE6C500BB9382 /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ALWAYS_SEARCH_USER_PATHS = NO; 280 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 281 | CLANG_CXX_LIBRARY = "libc++"; 282 | CLANG_ENABLE_MODULES = YES; 283 | CLANG_ENABLE_OBJC_ARC = YES; 284 | CLANG_WARN_BOOL_CONVERSION = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 287 | CLANG_WARN_EMPTY_BODY = YES; 288 | CLANG_WARN_ENUM_CONVERSION = YES; 289 | CLANG_WARN_INT_CONVERSION = YES; 290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 291 | CLANG_WARN_UNREACHABLE_CODE = YES; 292 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 293 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 294 | COPY_PHASE_STRIP = NO; 295 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 296 | ENABLE_STRICT_OBJC_MSGSEND = YES; 297 | ENABLE_TESTABILITY = YES; 298 | GCC_C_LANGUAGE_STANDARD = gnu99; 299 | GCC_DYNAMIC_NO_PIC = NO; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_OPTIMIZATION_LEVEL = 0; 302 | GCC_PREPROCESSOR_DEFINITIONS = ( 303 | "DEBUG=1", 304 | "$(inherited)", 305 | ); 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 313 | MTL_ENABLE_DEBUG_INFO = YES; 314 | ONLY_ACTIVE_ARCH = YES; 315 | SDKROOT = iphoneos; 316 | TARGETED_DEVICE_FAMILY = "1,2"; 317 | }; 318 | name = Debug; 319 | }; 320 | 67DA19541C8DE6C500BB9382 /* Release */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | VALIDATE_PRODUCT = YES; 355 | }; 356 | name = Release; 357 | }; 358 | 67DA19561C8DE6C500BB9382 /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 362 | INFOPLIST_FILE = "$(SRCROOT)/DemoSource/Info.plist"; 363 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 364 | PRODUCT_BUNDLE_IDENTIFIER = SteveBarnegren.DynamicWater; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | }; 367 | name = Debug; 368 | }; 369 | 67DA19571C8DE6C500BB9382 /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 373 | INFOPLIST_FILE = "$(SRCROOT)/DemoSource/Info.plist"; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | PRODUCT_BUNDLE_IDENTIFIER = SteveBarnegren.DynamicWater; 376 | PRODUCT_NAME = "$(TARGET_NAME)"; 377 | }; 378 | name = Release; 379 | }; 380 | /* End XCBuildConfiguration section */ 381 | 382 | /* Begin XCConfigurationList section */ 383 | 67DA19341C8DE6C400BB9382 /* Build configuration list for PBXProject "DynamicWater" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 67DA19531C8DE6C500BB9382 /* Debug */, 387 | 67DA19541C8DE6C500BB9382 /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | 67DA19551C8DE6C500BB9382 /* Build configuration list for PBXNativeTarget "DynamicWater" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | 67DA19561C8DE6C500BB9382 /* Debug */, 396 | 67DA19571C8DE6C500BB9382 /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | /* End XCConfigurationList section */ 402 | }; 403 | rootObject = 67DA19311C8DE6C400BB9382 /* Project object */; 404 | } 405 | --------------------------------------------------------------------------------