├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── .travis.yml ├── Dependencies └── UIBezierPath-Length │ ├── LICENSE.md │ ├── README.md │ ├── UIBezierPath+Length.h │ └── UIBezierPath+Length.m ├── LICENSE ├── Podfile ├── README.md ├── SBPlayer ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Beatmap │ └── BeatmapScanner.swift ├── Database │ ├── DB.swift │ └── FileEntry.swift ├── FS │ ├── FSScanner.swift │ └── FSWrapper.swift ├── Info.plist └── ViewController.swift ├── archive.sh ├── assets ├── approachcircle.afdesign ├── default-numbers.sketch ├── hit0.afdesign ├── hit100.afdesign ├── hit100k.afdesign ├── hit300.afdesign ├── hit300g.afdesign ├── hit300k.afdesign ├── hit50.afdesign ├── hitcircle.afdesign ├── hitcircleoverlay.afdesign ├── reversearrow.afdesign ├── slider-assets.sketch ├── sliderb.psd ├── sliderball_texture.psd └── ui_design.sketch ├── iosu.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── iosu.xcscheme ├── iosu.xcworkspace └── contents.xcworkspacedata ├── iosu ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-20-ipad.png │ │ ├── icon-20@2x-ipad.png │ │ ├── icon-20@2x.png │ │ ├── icon-20@3x.png │ │ ├── icon-29-ipad.png │ │ ├── icon-29@2x-ipad.png │ │ ├── icon-29@2x.png │ │ ├── icon-29@3x.png │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ └── icon-83.5@2x.png │ ├── Contents.json │ ├── approachcircle.imageset │ │ ├── Contents.json │ │ └── approachcircle.pdf │ ├── default-0.imageset │ │ ├── Contents.json │ │ ├── default-0.png │ │ ├── default-0@2x.png │ │ └── default-0@3x.png │ ├── default-1.imageset │ │ ├── Contents.json │ │ ├── default-1.png │ │ ├── default-1@2x.png │ │ └── default-1@3x.png │ ├── default-2.imageset │ │ ├── Contents.json │ │ ├── default-2.png │ │ ├── default-2@2x.png │ │ └── default-2@3x.png │ ├── default-3.imageset │ │ ├── Contents.json │ │ ├── default-3.png │ │ ├── default-3@2x.png │ │ └── default-3@3x.png │ ├── default-4.imageset │ │ ├── Contents.json │ │ ├── default-4.png │ │ ├── default-4@2x.png │ │ └── default-4@3x.png │ ├── default-5.imageset │ │ ├── Contents.json │ │ ├── default-5.png │ │ ├── default-5@2x.png │ │ └── default-5@3x.png │ ├── default-6.imageset │ │ ├── Contents.json │ │ ├── default-6.png │ │ ├── default-6@2x.png │ │ └── default-6@3x.png │ ├── default-7.imageset │ │ ├── Contents.json │ │ ├── default-7.png │ │ ├── default-7@2x.png │ │ └── default-7@3x.png │ ├── default-8.imageset │ │ ├── Contents.json │ │ ├── default-8.png │ │ ├── default-8@2x.png │ │ └── default-8@3x.png │ ├── default-9.imageset │ │ ├── Contents.json │ │ ├── default-9.png │ │ ├── default-9@2x.png │ │ └── default-9@3x.png │ ├── hit0.imageset │ │ ├── Contents.json │ │ ├── hit0.png │ │ ├── hit0@2x.png │ │ └── hit0@3x.png │ ├── hit100.imageset │ │ ├── Contents.json │ │ ├── hit100.png │ │ ├── hit100@2x.png │ │ └── hit100@3x.png │ ├── hit100k.imageset │ │ ├── Contents.json │ │ ├── hit100k.png │ │ ├── hit100k@2x.png │ │ └── hit100k@3x.png │ ├── hit300.imageset │ │ ├── Contents.json │ │ ├── hit300.png │ │ ├── hit300@2x.png │ │ └── hit300@3x.png │ ├── hit300g.imageset │ │ ├── Contents.json │ │ ├── hit300g.png │ │ ├── hit300g@2x.png │ │ └── hit300g@3x.png │ ├── hit300k.imageset │ │ ├── Contents.json │ │ ├── hit300k.png │ │ ├── hit300k@2x.png │ │ └── hit300k@3x.png │ ├── hit50.imageset │ │ ├── Contents.json │ │ ├── hit50.png │ │ ├── hit50@2x.png │ │ └── hit50@3x.png │ ├── hitcircle.imageset │ │ ├── Contents.json │ │ ├── hitcircle.png │ │ ├── hitcircle@2x.png │ │ └── hitcircle@3x.png │ ├── hitcircleoverlay.imageset │ │ ├── Contents.json │ │ ├── hitcircleoverlay.png │ │ ├── hitcircleoverlay@2x.png │ │ └── hitcircleoverlay@3x.png │ ├── reversearrow.imageset │ │ ├── Contents.json │ │ └── reversearrow.pdf │ ├── sliderb0.imageset │ │ ├── Contents.json │ │ ├── sliderb0.png │ │ ├── sliderb0@2x.png │ │ └── sliderb0@3x.png │ ├── sliderb1.imageset │ │ ├── Contents.json │ │ ├── sliderb1.png │ │ ├── sliderb1@2x.png │ │ └── sliderb1@3x.png │ ├── sliderb2.imageset │ │ ├── Contents.json │ │ ├── sliderb2.png │ │ ├── sliderb2@2x.png │ │ └── sliderb2@3x.png │ ├── sliderb3.imageset │ │ ├── Contents.json │ │ ├── sliderb3.png │ │ ├── sliderb3@2x.png │ │ └── sliderb3@3x.png │ ├── sliderb4.imageset │ │ ├── Contents.json │ │ ├── sliderb4.png │ │ ├── sliderb4@2x.png │ │ └── sliderb4@3x.png │ ├── sliderb5.imageset │ │ ├── Contents.json │ │ ├── sliderb5.png │ │ ├── sliderb5@2x.png │ │ └── sliderb5@3x.png │ ├── sliderb6.imageset │ │ ├── Contents.json │ │ ├── sliderb6.png │ │ ├── sliderb6@2x.png │ │ └── sliderb6@3x.png │ ├── sliderb7.imageset │ │ ├── Contents.json │ │ ├── sliderb7.png │ │ ├── sliderb7@2x.png │ │ └── sliderb7@3x.png │ ├── sliderb8.imageset │ │ ├── Contents.json │ │ ├── sliderb8.png │ │ ├── sliderb8@2x.png │ │ └── sliderb8@3x.png │ ├── sliderb9.imageset │ │ ├── Contents.json │ │ ├── sliderb9.png │ │ ├── sliderb9@2x.png │ │ └── sliderb9@3x.png │ ├── sliderfollowcircle.imageset │ │ ├── Contents.json │ │ ├── sliderfollowcircle.png │ │ ├── sliderfollowcircle@2x.png │ │ └── sliderfollowcircle@3x.png │ └── sliderscorepoint.imageset │ │ ├── Contents.json │ │ ├── sliderscorepoint.png │ │ ├── sliderscorepoint@2x.png │ │ └── sliderscorepoint@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Beatmap │ ├── BGVideo.swift │ ├── Beatmap.swift │ ├── BeatmapEnums.swift │ ├── Difficulty.swift │ ├── HitObjects │ │ ├── HitCircle.swift │ │ ├── HitObject.swift │ │ ├── HitObjectEnums.swift │ │ ├── Slider.swift │ │ ├── SliderBall.swift │ │ └── Spinner.swift │ ├── Renderer │ │ ├── ActionSet.swift │ │ ├── BundleImageBuffer.swift │ │ ├── CircleAction.swift │ │ ├── HitObjectAction.swift │ │ ├── RendererEnums.swift │ │ ├── SkinBuffer.swift │ │ └── SliderAction.swift │ ├── Scoring │ │ └── ScoringEnums.swift │ └── TimingPoint.swift ├── Database │ └── BeatmapScanner.swift ├── Info.plist ├── MediaPlayers │ ├── BGMusicPlayer.swift │ ├── BGVideoPlayer.swift │ ├── BundleAudioBuffer.swift │ └── PlaySoundFileNamedExt.swift ├── Scenes │ ├── GamePlayScene.swift │ ├── StoryBoardScene.swift │ └── TestScene.swift ├── StoryBoard │ ├── BasicImage.swift │ ├── MovingImage.swift │ ├── Renderer │ │ ├── Commands │ │ │ ├── SBColor.swift │ │ │ ├── SBFade.swift │ │ │ ├── SBLoop.swift │ │ │ ├── SBMove.swift │ │ │ ├── SBMoveX.swift │ │ │ ├── SBMoveY.swift │ │ │ ├── SBParam.swift │ │ │ ├── SBRotate.swift │ │ │ ├── SBScale.swift │ │ │ ├── SBTrigger.swift │ │ │ └── SBVScale.swift │ │ ├── Easing.swift │ │ ├── ImageBuffer.swift │ │ ├── SBCHelper.swift │ │ └── SBCommand.swift │ ├── StoryBoard.swift │ └── StoryBoardEnums.swift ├── ViewControllers │ ├── AlertView.swift │ ├── GameViewController.swift │ └── SelectionView.swift ├── audios │ ├── applause.mp3 │ ├── combobreak.mp3 │ ├── count.wav │ ├── count1s.wav │ ├── count2s.wav │ ├── count3s.wav │ ├── drum-hitclap.wav │ ├── drum-hitfinish.wav │ ├── drum-hitnormal.wav │ ├── drum-hitwhistle.wav │ ├── drum-sliderslide.wav │ ├── drum-slidertick.wav │ ├── drum-sliderwhistle.wav │ ├── failsound.mp3 │ ├── gos.wav │ ├── menuback.wav │ ├── menuclick.wav │ ├── menuhit.wav │ ├── normal-hitclap.wav │ ├── normal-hitfinish.wav │ ├── normal-hitnormal.wav │ ├── normal-hitwhistle.wav │ ├── normal-sliderslide.wav │ ├── normal-slidertick.wav │ ├── normal-sliderwhistle.wav │ ├── readys.wav │ ├── sectionfail.mp3 │ ├── sectionpass.mp3 │ ├── soft-hitclap.wav │ ├── soft-hitfinish.wav │ ├── soft-hitnormal.wav │ ├── soft-hitwhistle.wav │ ├── soft-sliderslide.wav │ ├── soft-slidertick.wav │ ├── soft-sliderwhistle.wav │ ├── spinnerbonus.wav │ └── spinnerspin.wav └── iosu-Bridging-Header.h ├── ipa.plist └── screenshots ├── import.png ├── p_run.png ├── sb_run.png ├── sbplayer1.png ├── sbplayer2.png ├── sbplayer3.png ├── sbplayer4.png ├── selection.png ├── sk_run.png └── v_run.png /.gitignore: -------------------------------------------------------------------------------- 1 | !*/ 2 | build/ 3 | Podfile.lock 4 | Pods/ 5 | *.icloud 6 | # Xcode 7 | # 8 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 9 | 10 | *.xcuserstate 11 | 12 | ## User settings 13 | xcuserdata/ 14 | 15 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 16 | *.xcscmblueprint 17 | *.xccheckout 18 | 19 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 20 | build/ 21 | DerivedData/ 22 | *.moved-aside 23 | *.pbxuser 24 | !default.pbxuser 25 | *.mode1v3 26 | !default.mode1v3 27 | *.mode2v3 28 | !default.mode2v3 29 | *.perspectivev3 30 | !default.perspectivev3 31 | # General 32 | .DS_Store 33 | .AppleDouble 34 | .LSOverride 35 | 36 | # Icon must end with two \r 37 | Icon 38 | 39 | 40 | # Thumbnails 41 | ._* 42 | 43 | # Files that might appear in the root of a volume 44 | .DocumentRevisions-V100 45 | .fseventsd 46 | .Spotlight-V100 47 | .TemporaryItems 48 | .Trashes 49 | .VolumeIcon.icns 50 | .com.apple.timemachine.donotpresent 51 | 52 | # Directories potentially created on remote AFP share 53 | .AppleDB 54 | .AppleDesktop 55 | Network Trash Folder 56 | Temporary Items 57 | .apdisk 58 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - archive 4 | 5 | build_project: 6 | stage: build 7 | script: 8 | - pod install 9 | - xcodebuild clean build -sdk iphonesimulator -workspace iosu.xcworkspace -scheme iosu CODE_SIGNING_REQUIRED=NO 10 | only: 11 | - tags 12 | - triggers 13 | 14 | archive_project: 15 | stage: archive 16 | script: 17 | - pod install 18 | - xcodebuild archive -workspace iosu.xcworkspace -scheme iosu -destination generic/platform=iOS -archivePath build/iosu.xcarchive 19 | - xcodebuild -exportArchive -archivePath build/iosu.xcarchive -exportPath build -exportOptionsPlist ipa.plist 20 | only: 21 | - tags 22 | - triggers 23 | artifacts: 24 | paths: 25 | - build/iosu.ipa -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/.gitmodules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode10.2 3 | 4 | install: 5 | - pod install --repo-update 6 | 7 | script: 8 | - xcodebuild -scheme iosu -workspace iosu.xcworkspace -configuration Release clean archive -archivePath "build/iosu.xcarchive" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO | xcpretty 9 | -------------------------------------------------------------------------------- /Dependencies/UIBezierPath-Length/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Maximilian Kraus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Dependencies/UIBezierPath-Length/README.md: -------------------------------------------------------------------------------- 1 | # UIBezierPath-Length 2 | A category on UIBezierPath that calculates the length and a point at a given length of the path. 3 | 4 | 5 | The MIT License (MIT) 6 | 7 | Copyright (c) 2015 Maximilian Kraus 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /Dependencies/UIBezierPath-Length/UIBezierPath+Length.h: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | 3 | @interface UIBezierPath (Length) 4 | 5 | - (CGFloat)length; 6 | 7 | - (CGPoint)pointAtPercentOfLength:(CGFloat)percent; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Dependencies/UIBezierPath-Length/UIBezierPath+Length.m: -------------------------------------------------------------------------------- 1 | #import "UIBezierPath+Length.h" 2 | 3 | typedef struct BezierSubpath { 4 | CGPoint startPoint; 5 | CGPoint controlPoint1; 6 | CGPoint controlPoint2; 7 | CGPoint endPoint; 8 | CGFloat length; 9 | CGPathElementType type; 10 | } BezierSubpath; 11 | 12 | typedef void(^BezierSubpathEnumerator)(const CGPathElement *element); 13 | 14 | static void bezierSubpathFunction(void *info, CGPathElement const *element) { 15 | BezierSubpathEnumerator block = (__bridge BezierSubpathEnumerator)info; 16 | block(element); 17 | } 18 | 19 | @implementation UIBezierPath (Length) 20 | 21 | #pragma mark - Internal 22 | 23 | - (void)enumerateSubpaths:(BezierSubpathEnumerator)enumeratorBlock 24 | { 25 | CGPathApply(self.CGPath, (__bridge void *)enumeratorBlock, bezierSubpathFunction); 26 | } 27 | 28 | - (NSUInteger)countSubpaths 29 | { 30 | __block NSUInteger count = 0; 31 | [self enumerateSubpaths:^(const CGPathElement *element) { 32 | if (element->type != kCGPathElementMoveToPoint) { 33 | count++; 34 | } 35 | }]; 36 | if (count == 0) { 37 | return 1; 38 | } 39 | return count; 40 | } 41 | 42 | - (void)extractSubpaths:(BezierSubpath*)subpathArray 43 | { 44 | __block CGPoint currentPoint = CGPointZero; 45 | __block NSUInteger i = 0; 46 | [self enumerateSubpaths:^(const CGPathElement *element) { 47 | 48 | CGPathElementType type = element->type; 49 | CGPoint *points = element->points; 50 | 51 | CGFloat subLength = 0.0f; 52 | CGPoint endPoint = CGPointZero; 53 | 54 | BezierSubpath subpath; 55 | subpath.type = type; 56 | subpath.startPoint = currentPoint; 57 | 58 | /* 59 | * All paths, no matter how complex, are created through a combination of these path elements. 60 | */ 61 | switch (type) { 62 | case kCGPathElementMoveToPoint: 63 | 64 | endPoint = points[0]; 65 | 66 | break; 67 | case kCGPathElementAddLineToPoint: 68 | 69 | endPoint = points[0]; 70 | 71 | subLength = linearLineLength(currentPoint, endPoint); 72 | 73 | break; 74 | case kCGPathElementAddQuadCurveToPoint: 75 | 76 | endPoint = points[1]; 77 | CGPoint controlPoint = points[0]; 78 | 79 | subLength = quadCurveLength(currentPoint, endPoint, controlPoint); 80 | 81 | subpath.controlPoint1 = controlPoint; 82 | 83 | break; 84 | case kCGPathElementAddCurveToPoint: 85 | 86 | endPoint = points[2]; 87 | CGPoint controlPoint1 = points[0]; 88 | CGPoint controlPoint2 = points[1]; 89 | 90 | subLength = cubicCurveLength(currentPoint, endPoint, controlPoint1, controlPoint2); 91 | 92 | subpath.controlPoint1 = controlPoint1; 93 | subpath.controlPoint2 = controlPoint2; 94 | 95 | break; 96 | case kCGPathElementCloseSubpath: 97 | default: 98 | break; 99 | } 100 | 101 | subpath.length = subLength; 102 | subpath.endPoint = endPoint; 103 | 104 | if (type != kCGPathElementMoveToPoint) { 105 | subpathArray[i] = subpath; 106 | i++; 107 | } 108 | 109 | currentPoint = endPoint; 110 | }]; 111 | if (i == 0) { 112 | subpathArray[0].length = 0.0f; 113 | subpathArray[0].endPoint = currentPoint; 114 | } 115 | } 116 | 117 | - (CGPoint)pointAtPercent:(CGFloat)t ofSubpath:(BezierSubpath)subpath { 118 | 119 | CGPoint p = CGPointZero; 120 | switch (subpath.type) { 121 | case kCGPathElementAddLineToPoint: 122 | p = linearBezierPoint(t, subpath.startPoint, subpath.endPoint); 123 | break; 124 | case kCGPathElementAddQuadCurveToPoint: 125 | p = quadBezierPoint(t, subpath.startPoint, subpath.controlPoint1, subpath.endPoint); 126 | break; 127 | case kCGPathElementAddCurveToPoint: 128 | p = cubicBezierPoint(t, subpath.startPoint, subpath.controlPoint1, subpath.controlPoint2, subpath.endPoint); 129 | break; 130 | default: 131 | break; 132 | } 133 | return p; 134 | } 135 | 136 | #pragma mark - Public API 137 | 138 | - (CGFloat)length { 139 | 140 | NSUInteger subpathCount = [self countSubpaths]; 141 | BezierSubpath subpaths[subpathCount]; 142 | [self extractSubpaths:subpaths]; 143 | 144 | CGFloat length = 0.0f; 145 | for (NSUInteger i = 0; i < subpathCount; i++) { 146 | length += subpaths[i].length; 147 | } 148 | return length; 149 | } 150 | 151 | - (CGPoint)pointAtPercentOfLength:(CGFloat)percent { 152 | 153 | if (percent < 0.0f) { 154 | percent = 0.0f; 155 | } else if (percent > 1.0f) { 156 | percent = 1.0f; 157 | } 158 | 159 | NSUInteger subpathCount = [self countSubpaths]; 160 | BezierSubpath subpaths[subpathCount]; 161 | [self extractSubpaths:subpaths]; 162 | 163 | CGFloat length = 0.0f; 164 | for (NSUInteger i = 0; i < subpathCount; i++) { 165 | length += subpaths[i].length; 166 | } 167 | 168 | CGFloat pointLocationInPath = length * percent; 169 | CGFloat currentLength = 0; 170 | BezierSubpath subpathContainingPoint; 171 | for (NSUInteger i = 0; i < subpathCount; i++) { 172 | if (currentLength + subpaths[i].length >= pointLocationInPath) { 173 | subpathContainingPoint = subpaths[i]; 174 | break; 175 | } else { 176 | currentLength += subpaths[i].length; 177 | } 178 | } 179 | 180 | CGFloat lengthInSubpath = pointLocationInPath - currentLength; 181 | if (subpathContainingPoint.length == 0) { 182 | return subpathContainingPoint.endPoint; 183 | } else { 184 | CGFloat t = lengthInSubpath / subpathContainingPoint.length; 185 | return [self pointAtPercent:t ofSubpath:subpathContainingPoint]; 186 | } 187 | } 188 | 189 | #pragma mark - Math helpers 190 | 191 | CGFloat linearLineLength(CGPoint fromPoint, CGPoint toPoint) { 192 | return sqrtf(powf(toPoint.x - fromPoint.x, 2) + powf(toPoint.y - fromPoint.y, 2)); 193 | } 194 | 195 | CGFloat quadCurveLength(CGPoint fromPoint, CGPoint toPoint, CGPoint controlPoint) { 196 | int iterations = 100; 197 | CGFloat length = 0; 198 | 199 | for (int idx=0; idx < iterations; idx++) { 200 | float t = idx * (1.0 / iterations); 201 | float tt = t + (1.0 / iterations); 202 | 203 | CGPoint p = quadBezierPoint(t, fromPoint, controlPoint, toPoint); 204 | CGPoint pp = quadBezierPoint(tt, fromPoint, controlPoint, toPoint); 205 | 206 | length += linearLineLength(p, pp); 207 | } 208 | 209 | return length; 210 | } 211 | 212 | CGFloat cubicCurveLength(CGPoint fromPoint, CGPoint toPoint, CGPoint controlPoint1, CGPoint controlPoint2) { 213 | int iterations = 100; 214 | CGFloat length = 0; 215 | 216 | for (int idx=0; idx < iterations; idx++) { 217 | float t = idx * (1.0 / iterations); 218 | float tt = t + (1.0 / iterations); 219 | 220 | CGPoint p = cubicBezierPoint(t, fromPoint, controlPoint1, controlPoint2, toPoint); 221 | CGPoint pp = cubicBezierPoint(tt, fromPoint, controlPoint1, controlPoint2, toPoint); 222 | 223 | length += linearLineLength(p, pp); 224 | } 225 | return length; 226 | } 227 | 228 | CGPoint linearBezierPoint(float t, CGPoint start, CGPoint end) { 229 | CGFloat dx = end.x - start.x; 230 | CGFloat dy = end.y - start.y; 231 | 232 | CGFloat px = start.x + (t * dx); 233 | CGFloat py = start.y + (t * dy); 234 | 235 | return CGPointMake(px, py); 236 | } 237 | 238 | CGPoint quadBezierPoint(float t, CGPoint start, CGPoint c1, CGPoint end) { 239 | CGFloat x = QuadBezier(t, start.x, c1.x, end.x); 240 | CGFloat y = QuadBezier(t, start.y, c1.y, end.y); 241 | 242 | return CGPointMake(x, y); 243 | } 244 | 245 | CGPoint cubicBezierPoint(float t, CGPoint start, CGPoint c1, CGPoint c2, CGPoint end) { 246 | CGFloat x = CubicBezier(t, start.x, c1.x, c2.x, end.x); 247 | CGFloat y = CubicBezier(t, start.y, c1.y, c2.y, end.y); 248 | 249 | return CGPointMake(x, y); 250 | } 251 | 252 | /* 253 | * http://ericasadun.com/2013/03/25/calculating-bezier-points/ 254 | */ 255 | float CubicBezier(float t, float start, float c1, float c2, float end) { 256 | CGFloat t_ = (1.0 - t); 257 | CGFloat tt_ = t_ * t_; 258 | CGFloat ttt_ = t_ * t_ * t_; 259 | CGFloat tt = t * t; 260 | CGFloat ttt = t * t * t; 261 | 262 | return start * ttt_ 263 | + 3.0 * c1 * tt_ * t 264 | + 3.0 * c2 * t_ * tt 265 | + end * ttt; 266 | } 267 | 268 | /* 269 | * http://ericasadun.com/2013/03/25/calculating-bezier-points/ 270 | */ 271 | float QuadBezier(float t, float start, float c1, float end) { 272 | CGFloat t_ = (1.0 - t); 273 | CGFloat tt_ = t_ * t_; 274 | CGFloat tt = t * t; 275 | 276 | return start * tt_ 277 | + 2.0 * c1 * t_ * t 278 | + end * tt; 279 | } 280 | 281 | @end 282 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 imxieyi 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 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | platform:ios, '10.0' 3 | 4 | target "iosu" do 5 | pod 'SpriteKitEasingSwift', :git => 'https://github.com/imxieyi/SpriteKitEasingSwift.git' 6 | pod 'MobileVLCKit' 7 | end 8 | 9 | target "SBPlayer" do 10 | pod 'Logging', '~> 1.0' 11 | pod 'SQLite.swift' 12 | end 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iosu! 2 | 3 | [![Build Status](https://travis-ci.org/imxieyi/iosu.svg?branch=master)](https://travis-ci.org/imxieyi/iosu) 4 | 5 | ## Introduction 6 | This is an ios port of the rhythm game [osu!](https://osu.ppy.sh). It is written in Swift based on [SpriteKit](https://developer.apple.com/spritekit/) framework. **It is just a hobby project for fun**. So don't expect that I can make it full functional in a short time. 7 | 8 | **Updated 2019-05-15:** The official [osu!lazer](https://github.com/ppy/osu) is already playable on iOS devices. 9 | 10 | ## Demo Video (Game+Storyboard) 11 | [https://youtu.be/RjuJyxciYkg](https://youtu.be/RjuJyxciYkg) 12 | 13 | ## What has been done 14 | - Scan beatmaps imported by iTunes 15 | - Decode .osu file (partly) 16 | - Timing with Quartz 17 | - Render background image & Background dim 18 | - Draw Plain HitCircle & Judge 19 | - Draw Sliders & Judge 20 | - **Play with backgound video** 21 | - **Play with StoryBoard** 22 | - **Play with skin in beatmap bundle** 23 | 24 | ## About UI design 25 | The sketch file of the UI design is available [here](https://github.com/imxieyi/iosu/blob/master/assets/ui_design.sketch). The implementation is available at [iosu_ui](https://github.com/imxieyi/iosu_ui/tree/master). This project also includes database and thumbnial cache implementation. 26 | 27 | ## About default skin 28 | According to the licence of [osu! default skin](https://osu.ppy.sh/forum/t/129191), the skin derived from osu! cannot be used here. So the skin used in this project is designed from scratch. All of the source files can be found under [assets/](https://github.com/imxieyi/iosu/tree/master/assets). In the future sound effects will also be replaced in order to prevent possible violations. 29 | 30 | ## Compilation 31 | 1. Install the latest version of XCode. 32 | 2. Install [CocoaPods](https://cocoapods.org/). 33 | 3. Run the following commands: 34 | ```bash 35 | git clone https://github.com/imxieyi/iosu.git 36 | cd iosu 37 | pod install 38 | ``` 39 | 4. Open iosu.xcworkspace in the project directory. 40 | 5. Build. 41 | 42 | ## Installation Through Cydia Impactor (Not recommended) (iOS 10.0+ required) 43 | 1. Download [Cydia Impactor](http://www.cydiaimpactor.com/). 44 | 2. Download latest [ipa artifact](https://git.imxieyi.com/xieyi/iosu/tags) from my personal gitlab server. (Direct link to ipa file: [https://git.imxieyi.com/xieyi/iosu/-/jobs/artifacts/v0.3.1/download?job=archive_project](https://git.imxieyi.com/xieyi/iosu/-/jobs/artifacts/v0.3.1/download?job=archive_project)) 45 | 3. Connect your iDevice and run Cydia Impactor. 46 | 4. Drag .ipa file to Cydia Impactor window. 47 | 5. Input your Apple ID info. 48 | 6. Wait until it finishes. 49 | 50 | ## Import Beatmap 51 | ![](screenshots/import.png) 52 | 53 | Both osu and osb files should be contained in directories shown in the iTunes. Directly importing directories in `Songs` on your PC (can be found in osu! installation path) should work fine. 54 | 55 | ## Notice 56 | 0. This project is still at **super early stage**, there are plenty of bugs among the whole application. So don't worry if it crashes constantly on your device. If you want to report, please attach **the link to the beatmap on [osu.ppy.sh](https://osu.ppy.sh/)** while creating an issue. 57 | 1. Currently there is no database, so please do not import too many beatmaps. 58 | 2. **It's okay** to play a beatmap with too complicated storyboard as background now. However due to thread synchronization issue, unused SKNodes will not be released during gameplay. This would result in more memory usage depending on beatmap and storyboard. 59 | 3. ~~The patched PlaySoundFileNamed function has some bugs that may crash the application, I'm still finding the solution.~~ (should have been fixed in 43e862) 60 | 4. I have an iPhone6s and an iPad Pro 10.5 to test, so it may works well on all iDevices. 61 | 5. If the framerate drops under 10, the timing will be inaccurate. So do not try to run it in the simulator. If that happens on your device, maybe you can consider update it because of low performance. 62 | 6. I have modified the SpriteKitEasingSwift framework to meet the need of osu! storyboard. The [modified version](https://github.com/imxieyi/SpriteKitEasingSwift) can also be installed by CocoaPods. 63 | 7. With the help of image buffer, you can load any complicated storyboard with acceptable memory usage. **But be sure to correct storyboard image paths because in Unix-like system file name is case sensitive.** Also, there are still some bugs in the render and processor. But in most of the time it works well. 64 | 8. StoryBoard support for [Cheat Sheet](https://osu.ppy.sh/wiki/Storyboard_Scripting/Cheat_Sheet) is incomplete. Loading such storyboards might crash the application. 65 | 9. I'm new to iOS development, Swift and SpriteKit. And I don't like to insert a lot of comments. So please tolerate my awful code. 66 | 67 | ## Selection View Screenshot 68 | 69 | ![](screenshots/selection.png) 70 | 71 | ## GamePlay Screenshot 72 | 73 | **With background image:** 74 | 75 | ![](screenshots/p_run.png) 76 | 77 | beatmap: [fourfolium - Now Loading!!!!](https://osu.ppy.sh/s/488630) 78 | 79 | **With skin in beatmap bundle:** 80 | 81 | ![](screenshots/sk_run.png) 82 | 83 | beatmap: [DJ Okawari - Flower Dance](https://osu.ppy.sh/s/33688) 84 | 85 | **With background video:** 86 | 87 | ![](screenshots/v_run.png) 88 | 89 | beatmap: [Hatsune Miku - Ievan Polkka](https://osu.ppy.sh/s/1287) 90 | 91 | **With background storyboard:** 92 | 93 | ![](screenshots/sb_run.png) 94 | 95 | beatmap: [fhana - Outside of Melancholy ~Yuuutsu no Mukougawa~](https://osu.ppy.sh/s/568455) 96 | 97 | ## StoryBoard Screenshot 98 | 99 | ![](screenshots/sbplayer1.png) 100 | 101 | beatmap: [Tatsumi Megumi featured by Sano Hiroaki - Tsubomi (Long Version)](https://osu.ppy.sh/s/311064) 102 | 103 | ![](screenshots/sbplayer2.png) 104 | 105 | ![](screenshots/sbplayer3.png) 106 | 107 | beatmap: [Kana Nishino - Sweet Dreams (11t dnb mix)](https://osu.ppy.sh/s/499488) 108 | 109 | ![](screenshots/sbplayer4.png) 110 | 111 | beatmap: [DM vs. POCKET - uNDeRWoRLD MoNaRCHy](https://osu.ppy.sh/s/412938) 112 | 113 | ## Credit 114 | - [osu!](https://osu.ppy.sh) 115 | - [opsu!](https://github.com/itdelatrisu/opsu) 116 | - [osu-parser](https://github.com/nojhamster/osu-parser) 117 | - [SpriteKitEasingSwift](https://github.com/craiggrummitt/SpriteKitEasingSwift) 118 | - [MobileVLCKit](https://cocoapods.org/pods/MobileVLCKit) 119 | - ~~[KSYMediaPlayer_iOS](https://github.com/ksvc/KSYMediaPlayer_iOS)~~ (Abandoned because of compatibility issue) 120 | - [SQLite.swift](https://github.com/stephencelis/SQLite.swift) 121 | - [UIBezierPath-Length](https://github.com/ImJCabus/UIBezierPath-Length) 122 | - [Source Han Sans](https://github.com/adobe-fonts/source-han-sans) 123 | - [Stack Overflow](http://stackoverflow.com) 124 | - [Apple Developer Documentation](https://developer.apple.com/reference/) 125 | -------------------------------------------------------------------------------- /SBPlayer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SBPlayer/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SBPlayer/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SBPlayer/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 | -------------------------------------------------------------------------------- /SBPlayer/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 | 29 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /SBPlayer/Beatmap/BeatmapScanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BeatmapScanner.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Logging 11 | 12 | class BeatmapScanner { 13 | 14 | let logger = Logger(label: "BeatmapScanner") 15 | 16 | let fsScanner: FSScanner 17 | 18 | init(fsScanner: FSScanner) { 19 | self.fsScanner = fsScanner 20 | } 21 | 22 | func scan() { 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /SBPlayer/Database/DB.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DB.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SQLite 11 | import Logging 12 | 13 | class DB { 14 | 15 | public static private(set) var shared: DB! 16 | 17 | let connection: Connection 18 | 19 | static let logger = Logger(label: "Database") 20 | 21 | static func initialize() throws { 22 | if shared == nil { 23 | shared = try DB() 24 | try FileEntry.createTable(shared.connection) 25 | } 26 | } 27 | 28 | private init() throws { 29 | let libPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] 30 | let dbPath = libPath.appendingPathComponent("iosu.db") 31 | DB.logger.info("database path: \(dbPath.path)") 32 | connection = try Connection(dbPath.path) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /SBPlayer/Database/FileEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileEntry.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SQLite 11 | import Logging 12 | 13 | struct FileEntry { 14 | 15 | private static let table = Table("fs") 16 | 17 | // Database table columns 18 | static let ID = Expression("id") 19 | static let RelativePath = Expression("relativePath") 20 | static let LowerCasePath = Expression("lowerCasePath") 21 | static let Size = Expression("size") 22 | static let LastModification = Expression("lastModification") 23 | 24 | // Class fields 25 | let id: Int64 26 | let relativePath: String 27 | let size: Int64 28 | let lastModification: Date 29 | 30 | static func createTable(_ connection: Connection) throws { 31 | let tableBuilder = FileEntry.table.create(ifNotExists: true) { tb in 32 | tb.column(ID, primaryKey: .autoincrement) 33 | tb.column(RelativePath, unique: true) 34 | tb.column(LowerCasePath) 35 | tb.column(Size) 36 | tb.column(LastModification) 37 | } 38 | try connection.run(tableBuilder) 39 | var indexBuilder = FileEntry.table.createIndex(RelativePath, unique: true, ifNotExists: true) 40 | try connection.run(indexBuilder) 41 | indexBuilder = FileEntry.table.createIndex(LowerCasePath, unique: false, ifNotExists: true) 42 | try connection.run(indexBuilder) 43 | } 44 | 45 | init(id: Int64 = 0, relativePath: String, size: Int64, lastModification: Date) { 46 | self.id = id 47 | self.relativePath = relativePath 48 | self.size = size 49 | self.lastModification = lastModification 50 | } 51 | 52 | func insert() -> FileEntry? { 53 | let insert = FileEntry.table.insert(FileEntry.RelativePath <- relativePath, 54 | FileEntry.LowerCasePath <- relativePath.lowercased(), 55 | FileEntry.Size <- size, 56 | FileEntry.LastModification <- lastModification) 57 | do { 58 | let id = try DB.shared.connection.run(insert) 59 | return FileEntry.get(id) 60 | } catch let error { 61 | DB.logger.error("\(error)") 62 | } 63 | return nil 64 | } 65 | 66 | private static func get(_ row: Row) -> FileEntry { 67 | return FileEntry(id: row[FileEntry.ID], relativePath: row[FileEntry.RelativePath], 68 | size: row[FileEntry.Size], lastModification: row[FileEntry.LastModification]) 69 | } 70 | 71 | static func get(_ relativePath: String, ignoreCase: Bool = true) -> FileEntry? { 72 | do { 73 | // First try case sensitive 74 | var filtered = FileEntry.table.filter(FileEntry.RelativePath == relativePath) 75 | for row in try DB.shared.connection.prepare(filtered) { 76 | return get(row) 77 | } 78 | // If failed, try case insensitive 79 | if ignoreCase { 80 | filtered = FileEntry.table.filter(FileEntry.LowerCasePath == relativePath.lowercased()) 81 | for row in try DB.shared.connection.prepare(filtered) { 82 | let actualPath = row[FileEntry.RelativePath] 83 | DB.logger.debug("\(relativePath) should be \(actualPath)") 84 | return get(row) 85 | } 86 | } 87 | } catch let error { 88 | DB.logger.error("\(error)") 89 | } 90 | return nil 91 | } 92 | 93 | static func get(_ id: Int64) -> FileEntry? { 94 | do { 95 | let filtered = FileEntry.table.filter(FileEntry.ID == id) 96 | for row in try DB.shared.connection.prepare(filtered) { 97 | return get(row) 98 | } 99 | } catch let error { 100 | DB.logger.error("\(error)") 101 | } 102 | return nil 103 | } 104 | 105 | static func forEach(_ callback: (FileEntry) -> Void) { 106 | do { 107 | for row in try DB.shared.connection.prepare(table) { 108 | callback(get(row)) 109 | } 110 | } catch let error { 111 | DB.logger.error("\(error)") 112 | } 113 | } 114 | 115 | func update(size: Int64, lastModification: Date) -> FileEntry? { 116 | let update = FileEntry.table.filter(FileEntry.ID == id) 117 | .update(FileEntry.Size <- size, 118 | FileEntry.LastModification <- lastModification) 119 | do { 120 | try DB.shared.connection.run(update) 121 | return FileEntry.get(id) 122 | } catch let error { 123 | DB.logger.error("\(error)") 124 | } 125 | return nil 126 | } 127 | 128 | func delete() { 129 | do { 130 | let filtered = FileEntry.table.filter(FileEntry.ID == id) 131 | try DB.shared.connection.run(filtered.delete()) 132 | } catch let error { 133 | DB.logger.error("\(error)") 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /SBPlayer/FS/FSScanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FSScanner.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Logging 11 | 12 | class FSScanner { 13 | 14 | let logger: Logger 15 | let documentPath: URL 16 | 17 | var fileIDList = Set() 18 | 19 | // For further processing 20 | var newFileList: [FileEntry] = [] 21 | var deletedFileList: [FileEntry] = [] 22 | var updatedFileList: [FileEntry] = [] 23 | 24 | init() { 25 | logger = Logger(label: "FileSystem") 26 | documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] 27 | } 28 | 29 | func scan() { 30 | fileIDList.removeAll() 31 | newFileList = [] 32 | deletedFileList = [] 33 | updatedFileList = [] 34 | logger.info("Start FS scanner: \(documentPath.path)") 35 | scan(documentPath.path, prefix: "") 36 | logger.info("Scanning for deleted files") 37 | FileEntry.forEach { (entry) in 38 | if !fileIDList.contains(entry.id) { 39 | entry.delete() 40 | deletedFileList.append(entry) 41 | } 42 | } 43 | logger.info("Finished FS scanner. \(newFileList.count) added, \(deletedFileList.count) deleted, \(updatedFileList.count) updated") 44 | } 45 | 46 | private func scan(_ root: String, prefix: String) { 47 | do { 48 | let entries = try FileManager.default.contentsOfDirectory(atPath: root) 49 | var isDirectory = ObjCBool(true) 50 | for entry in entries { 51 | let relativePath = prefix + (prefix == "" ? "" : "/") + URL(fileURLWithPath: entry).lastPathComponent 52 | let absolutePath = documentPath.path + "/" + relativePath 53 | if !FileManager.default.fileExists(atPath: absolutePath, isDirectory: &isDirectory) { 54 | continue 55 | } 56 | if isDirectory.boolValue { 57 | scan(absolutePath, prefix: relativePath) 58 | } else { 59 | do { 60 | let attrs = try FileManager.default.attributesOfItem(atPath: absolutePath) 61 | let size = Int64(truncating: attrs[.size] as? NSNumber ?? 0) 62 | let modificationDate = (attrs[.modificationDate] as? NSDate) as Date? ?? Date(timeIntervalSince1970: 0) 63 | if let fileEntry = FileEntry.get(relativePath, ignoreCase: false) { 64 | // Entry exists in database 65 | if fileEntry.size != size || fileEntry.lastModification != modificationDate { 66 | logger.debug("Updated: \(relativePath)") 67 | if let newEntry = fileEntry.update(size: size, lastModification: modificationDate) { 68 | updatedFileList.append(newEntry) 69 | fileIDList.insert(newEntry.id) 70 | } else { 71 | logger.error("Failed to update metadata for \(relativePath)") 72 | } 73 | } else { 74 | logger.debug("Existed: \(relativePath)") 75 | fileIDList.insert(fileEntry.id) 76 | } 77 | } else { 78 | // New entry 79 | let fileEntry = FileEntry(relativePath: relativePath, size: size, lastModification: modificationDate) 80 | if let newEntry = fileEntry.insert() { 81 | logger.debug("New: \(relativePath)") 82 | newFileList.append(newEntry) 83 | fileIDList.insert(newEntry.id) 84 | } else { 85 | logger.error("Failed to insert new entry: \(relativePath)") 86 | } 87 | } 88 | } catch let error { 89 | logger.error("Failed to get metadata from \(relativePath): \(error.localizedDescription)") 90 | } 91 | } 92 | } 93 | } catch let error { 94 | logger.error("Failed to scan \(root)") 95 | logger.error("\(error)") 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /SBPlayer/FS/FSWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FSWrapper.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FSWrapper { 12 | 13 | static func getRealPath(_ path: String) -> String? { 14 | return FileEntry.get(path)?.relativePath 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /SBPlayer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SBPlayer/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // SBPlayer 4 | // 5 | // Created by 谢宜 on 2019/5/15. 6 | // Copyright © 2019 xieyi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view. 16 | do { 17 | try DB.initialize() 18 | let scanner = FSScanner() 19 | scanner.scan() 20 | } catch let error { 21 | DB.logger.critical("Failed to initialize database: \(error.localizedDescription)") 22 | } 23 | } 24 | 25 | @IBAction func deleteDB(_ sender: Any) { 26 | let libPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] 27 | let dbPath = libPath.appendingPathComponent("iosu.db") 28 | try! FileManager.default.removeItem(at: dbPath) 29 | } 30 | 31 | @IBAction func copyDB(_ sender: Any) { 32 | let libPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] 33 | let dbPath = libPath.appendingPathComponent("iosu.db") 34 | let docPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] 35 | let copyPath = docPath.appendingPathComponent("iosu.db") 36 | try! FileManager.default.copyItem(at: dbPath, to: copyPath) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /archive.sh: -------------------------------------------------------------------------------- 1 | rm -rf build/ 2 | xcodebuild archive -workspace iosu.xcworkspace -scheme iosu -destination generic/platform=iOS -archivePath build/iosu.xcarchive 3 | xcodebuild -exportArchive -archivePath build/iosu.xcarchive -exportPath build -exportOptionsPlist ipa.plist 4 | mv build/iosu.ipa build/iosu_$(git tag|tail -1).ipa 5 | -------------------------------------------------------------------------------- /assets/approachcircle.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/approachcircle.afdesign -------------------------------------------------------------------------------- /assets/default-numbers.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/default-numbers.sketch -------------------------------------------------------------------------------- /assets/hit0.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit0.afdesign -------------------------------------------------------------------------------- /assets/hit100.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit100.afdesign -------------------------------------------------------------------------------- /assets/hit100k.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit100k.afdesign -------------------------------------------------------------------------------- /assets/hit300.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit300.afdesign -------------------------------------------------------------------------------- /assets/hit300g.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit300g.afdesign -------------------------------------------------------------------------------- /assets/hit300k.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit300k.afdesign -------------------------------------------------------------------------------- /assets/hit50.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hit50.afdesign -------------------------------------------------------------------------------- /assets/hitcircle.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hitcircle.afdesign -------------------------------------------------------------------------------- /assets/hitcircleoverlay.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/hitcircleoverlay.afdesign -------------------------------------------------------------------------------- /assets/reversearrow.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/reversearrow.afdesign -------------------------------------------------------------------------------- /assets/slider-assets.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/slider-assets.sketch -------------------------------------------------------------------------------- /assets/sliderb.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/sliderb.psd -------------------------------------------------------------------------------- /assets/sliderball_texture.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/sliderball_texture.psd -------------------------------------------------------------------------------- /assets/ui_design.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/assets/ui_design.sketch -------------------------------------------------------------------------------- /iosu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iosu.xcodeproj/xcshareddata/xcschemes/iosu.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 87 | 93 | 94 | 95 | 96 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /iosu.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iosu/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/3/28. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "icon-29@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "icon-29@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "icon-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "icon-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "icon-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "icon-20-ipad.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "icon-20@2x-ipad.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "icon-29-ipad.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "icon-29@2x-ipad.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "icon-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "icon-40@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "icon-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "icon-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "icon-83.5@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "idiom" : "ios-marketing", 107 | "size" : "1024x1024", 108 | "scale" : "1x" 109 | } 110 | ], 111 | "info" : { 112 | "version" : 1, 113 | "author" : "xcode" 114 | } 115 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/approachcircle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "approachcircle.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/approachcircle.imageset/approachcircle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/approachcircle.imageset/approachcircle.pdf -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-0.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-0@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-0@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-0.imageset/default-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-0.imageset/default-0.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-0.imageset/default-0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-0.imageset/default-0@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-0.imageset/default-0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-0.imageset/default-0@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-1@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-1@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-1.imageset/default-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-1.imageset/default-1.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-1.imageset/default-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-1.imageset/default-1@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-1.imageset/default-1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-1.imageset/default-1@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-2@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-2@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-2.imageset/default-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-2.imageset/default-2.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-2.imageset/default-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-2.imageset/default-2@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-2.imageset/default-2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-2.imageset/default-2@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-3@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-3@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-3.imageset/default-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-3.imageset/default-3.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-3.imageset/default-3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-3.imageset/default-3@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-3.imageset/default-3@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-3.imageset/default-3@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-4@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-4@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-4.imageset/default-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-4.imageset/default-4.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-4.imageset/default-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-4.imageset/default-4@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-4.imageset/default-4@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-4.imageset/default-4@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-5@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-5@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-5.imageset/default-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-5.imageset/default-5.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-5.imageset/default-5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-5.imageset/default-5@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-5.imageset/default-5@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-5.imageset/default-5@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-6.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-6@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-6@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-6.imageset/default-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-6.imageset/default-6.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-6.imageset/default-6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-6.imageset/default-6@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-6.imageset/default-6@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-6.imageset/default-6@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-7.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-7@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-7@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-7.imageset/default-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-7.imageset/default-7.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-7.imageset/default-7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-7.imageset/default-7@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-7.imageset/default-7@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-7.imageset/default-7@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-8.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-8@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-8@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-8.imageset/default-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-8.imageset/default-8.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-8.imageset/default-8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-8.imageset/default-8@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-8.imageset/default-8@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-8.imageset/default-8@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default-9.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-9@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-9@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-9.imageset/default-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-9.imageset/default-9.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-9.imageset/default-9@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-9.imageset/default-9@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/default-9.imageset/default-9@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/default-9.imageset/default-9@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit0.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit0@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit0@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit0.imageset/hit0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit0.imageset/hit0.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit0.imageset/hit0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit0.imageset/hit0@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit0.imageset/hit0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit0.imageset/hit0@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit100.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit100@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit100@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100.imageset/hit100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit100.imageset/hit100.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100.imageset/hit100@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit100.imageset/hit100@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100.imageset/hit100@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit100.imageset/hit100@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100k.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit100k.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit100k@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit100k@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100k.imageset/hit100k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit100k.imageset/hit100k.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100k.imageset/hit100k@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit100k.imageset/hit100k@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit100k.imageset/hit100k@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit100k.imageset/hit100k@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit300.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit300@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit300@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300.imageset/hit300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300.imageset/hit300.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300.imageset/hit300@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300.imageset/hit300@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300.imageset/hit300@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300.imageset/hit300@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300g.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit300g.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit300g@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit300g@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300g.imageset/hit300g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300g.imageset/hit300g.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300g.imageset/hit300g@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300g.imageset/hit300g@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300g.imageset/hit300g@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300g.imageset/hit300g@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300k.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit300k.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit300k@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit300k@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300k.imageset/hit300k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300k.imageset/hit300k.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300k.imageset/hit300k@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300k.imageset/hit300k@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit300k.imageset/hit300k@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit300k.imageset/hit300k@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit50.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hit50.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hit50@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hit50@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit50.imageset/hit50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit50.imageset/hit50.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit50.imageset/hit50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit50.imageset/hit50@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hit50.imageset/hit50@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hit50.imageset/hit50@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hitcircle.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hitcircle@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hitcircle@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircle.imageset/hitcircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hitcircle.imageset/hitcircle.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircle.imageset/hitcircle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hitcircle.imageset/hitcircle@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircle.imageset/hitcircle@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hitcircle.imageset/hitcircle@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircleoverlay.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "hitcircleoverlay.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "hitcircleoverlay@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "hitcircleoverlay@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircleoverlay.imageset/hitcircleoverlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hitcircleoverlay.imageset/hitcircleoverlay.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircleoverlay.imageset/hitcircleoverlay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hitcircleoverlay.imageset/hitcircleoverlay@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/hitcircleoverlay.imageset/hitcircleoverlay@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/hitcircleoverlay.imageset/hitcircleoverlay@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/reversearrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "reversearrow.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/reversearrow.imageset/reversearrow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/reversearrow.imageset/reversearrow.pdf -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb0.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb0@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb0@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb0.imageset/sliderb0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb0.imageset/sliderb0.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb0.imageset/sliderb0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb0.imageset/sliderb0@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb0.imageset/sliderb0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb0.imageset/sliderb0@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb1@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb1@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb1.imageset/sliderb1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb1.imageset/sliderb1.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb1.imageset/sliderb1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb1.imageset/sliderb1@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb1.imageset/sliderb1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb1.imageset/sliderb1@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb2@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb2@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb2.imageset/sliderb2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb2.imageset/sliderb2.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb2.imageset/sliderb2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb2.imageset/sliderb2@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb2.imageset/sliderb2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb2.imageset/sliderb2@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb3@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb3@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb3.imageset/sliderb3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb3.imageset/sliderb3.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb3.imageset/sliderb3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb3.imageset/sliderb3@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb3.imageset/sliderb3@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb3.imageset/sliderb3@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb4@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb4@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb4.imageset/sliderb4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb4.imageset/sliderb4.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb4.imageset/sliderb4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb4.imageset/sliderb4@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb4.imageset/sliderb4@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb4.imageset/sliderb4@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb5@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb5@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb5.imageset/sliderb5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb5.imageset/sliderb5.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb5.imageset/sliderb5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb5.imageset/sliderb5@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb5.imageset/sliderb5@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb5.imageset/sliderb5@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb6.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb6@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb6@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb6.imageset/sliderb6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb6.imageset/sliderb6.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb6.imageset/sliderb6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb6.imageset/sliderb6@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb6.imageset/sliderb6@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb6.imageset/sliderb6@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb7.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb7@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb7@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb7.imageset/sliderb7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb7.imageset/sliderb7.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb7.imageset/sliderb7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb7.imageset/sliderb7@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb7.imageset/sliderb7@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb7.imageset/sliderb7@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb8.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb8@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb8@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb8.imageset/sliderb8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb8.imageset/sliderb8.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb8.imageset/sliderb8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb8.imageset/sliderb8@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb8.imageset/sliderb8@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb8.imageset/sliderb8@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderb9.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderb9@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderb9@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb9.imageset/sliderb9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb9.imageset/sliderb9.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb9.imageset/sliderb9@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb9.imageset/sliderb9@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderb9.imageset/sliderb9@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderb9.imageset/sliderb9@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderfollowcircle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderfollowcircle.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderfollowcircle@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderfollowcircle@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderfollowcircle.imageset/sliderfollowcircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderfollowcircle.imageset/sliderfollowcircle.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderfollowcircle.imageset/sliderfollowcircle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderfollowcircle.imageset/sliderfollowcircle@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderfollowcircle.imageset/sliderfollowcircle@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderfollowcircle.imageset/sliderfollowcircle@3x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderscorepoint.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sliderscorepoint.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "sliderscorepoint@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "sliderscorepoint@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderscorepoint.imageset/sliderscorepoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderscorepoint.imageset/sliderscorepoint.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderscorepoint.imageset/sliderscorepoint@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderscorepoint.imageset/sliderscorepoint@2x.png -------------------------------------------------------------------------------- /iosu/Assets.xcassets/sliderscorepoint.imageset/sliderscorepoint@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/Assets.xcassets/sliderscorepoint.imageset/sliderscorepoint@3x.png -------------------------------------------------------------------------------- /iosu/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 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /iosu/Beatmap/BGVideo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BGVideo.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BGVideo { 12 | 13 | var file:String 14 | var time:Int 15 | 16 | init(file:String,time:Int) { 17 | self.file=file 18 | self.time=time 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /iosu/Beatmap/BeatmapEnums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BeatmapEnums.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SampleSet { 12 | case auto 13 | case normal 14 | case soft 15 | case drum 16 | } 17 | 18 | enum BeatmapError:Error{ 19 | case fileNotFound 20 | case illegalFormat 21 | case noAudioFile 22 | case noTimingPoints 23 | case audioFileNotExist 24 | case noColor 25 | case noHitObject 26 | } 27 | -------------------------------------------------------------------------------- /iosu/Beatmap/Difficulty.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Difficulty.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/9. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BMDifficulty { 12 | 13 | fileprivate let ARFuncA:Double = -120 14 | fileprivate let ARFuncB:Double = 1800 15 | fileprivate let S300FuncA:Double = -6 16 | fileprivate let S300FuncB:Double = 79.5 17 | fileprivate let S100FuncA:Double = -8 18 | fileprivate let S100FuncB:Double = 139.5 19 | fileprivate let S50FuncA:Double = -10 20 | fileprivate let S50FuncB:Double = 199.5 21 | 22 | fileprivate let HPDrainRate:Double 23 | fileprivate let CircleSize:Double 24 | fileprivate let OverallDifficulty:Double 25 | fileprivate let ApproachRate:Double 26 | public let SliderMultiplier:Double 27 | public let SliderTickRate:Double 28 | 29 | public let AbsoluteCS:Double 30 | 31 | public let ARTime:Double 32 | public let Score300:Double 33 | public let Score100:Double 34 | public let Score50:Double 35 | 36 | init(HP:Double,CS:Double,OD:Double,AR:Double,SM:Double,ST:Double) { 37 | HPDrainRate=HP 38 | CircleSize=CS 39 | OverallDifficulty=OD 40 | ApproachRate=AR 41 | SliderMultiplier=SM 42 | SliderTickRate=ST 43 | ARTime=ARFuncB+ARFuncA*AR 44 | Score300=S300FuncB+S300FuncA*OD 45 | Score100=S100FuncB+S100FuncA*OD 46 | Score50=S50FuncB+S50FuncA*OD 47 | AbsoluteCS=GamePlayScene.conv(w: 108.848-CS*8.9646) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /iosu/Beatmap/HitObjects/HitCircle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HitCircle.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class HitCircle:HitObject{ 12 | 13 | var ctype:CircleType = .plain 14 | 15 | init(x:Int,y:Int,time:Int,hitsound:Int,newCombo:Bool) { 16 | super.init(type: .circle, x: x, y: y, time: time, hitsound: HitObject.hitsoundDecode(hitsound), newcombo: newCombo) 17 | } 18 | 19 | init(x:Int,y:Int,time:Int,hitsound:Int,newCombo:Bool,type:CircleType) { 20 | self.ctype=type 21 | super.init(type: .circle, x: x, y: y, time: time, hitsound: HitObject.hitsoundDecode(hitsound), newcombo: newCombo) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /iosu/Beatmap/HitObjects/HitObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HitObject.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class HitObject{ 12 | 13 | var type:HitObjectType 14 | var x:Int //0~512 15 | var y:Int //0~384 16 | var time:Int //Milliseconds from beginning of song 17 | var hitSound:HitSound 18 | var newCombo:Bool 19 | 20 | init(type:HitObjectType,x:Int,y:Int,time:Int,hitsound:HitSound,newcombo:Bool) { 21 | self.type=type 22 | //debugPrint("before:\(x),\(y)") 23 | self.x=Int(GamePlayScene.conv(x: Double(x))) 24 | self.y=Int(GamePlayScene.conv(y: Double(y))) 25 | //debugPrint("after:\(self.x),\(self.y)") 26 | self.time=time 27 | self.hitSound = hitsound 28 | self.newCombo=newcombo 29 | } 30 | 31 | static func getObjectType(_ num:Int) -> HitObjectType { 32 | if num==1 || num==5 { 33 | return HitObjectType.circle 34 | } 35 | if num==2 || num==6 { 36 | return HitObjectType.slider 37 | } 38 | if num==8 || num==12 { 39 | return HitObjectType.spinner 40 | } 41 | return HitObjectType.none 42 | } 43 | 44 | static func getNewCombo(_ num:Int) -> Bool { 45 | if num==5 || num==6 || num==12 { 46 | return true 47 | } 48 | return false 49 | } 50 | 51 | static func hitsoundDecode(_ num:Int) -> HitSound { 52 | switch num { 53 | case 0: 54 | return .normal 55 | case 2: 56 | return .whistle 57 | case 4: 58 | return .finish 59 | case 8: 60 | return .clap 61 | default: 62 | //Maybe there are other types 63 | return .normal 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /iosu/Beatmap/HitObjects/HitObjectEnums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HitObjectEnums.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HitSound { 12 | case normal //0 13 | case whistle //2 14 | case finish //4 15 | case clap //8 16 | } 17 | 18 | enum HitObjectType{ 19 | case circle 20 | case slider 21 | case spinner 22 | case none //Unknown hitobject type 23 | } 24 | 25 | enum CircleType{ 26 | case plain 27 | case sliderHead 28 | case sliderEnd 29 | case sliderArrow 30 | } 31 | 32 | enum SliderType{ 33 | case linear //L 34 | case passThrough //P 35 | case bezier //B 36 | case catmull //C 37 | case none //Invalid slider type 38 | } 39 | -------------------------------------------------------------------------------- /iosu/Beatmap/HitObjects/SliderBall.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SliderBall.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class SliderBall { 13 | 14 | let sliderball1=SKSpriteNode(texture: SliderBall.sliderballimg("sliderb0")) 15 | var followcircle=SKSpriteNode(texture: SKTexture(imageNamed: "sliderfollowcircle")) 16 | let scene:SKScene 17 | var useSkin = false 18 | 19 | init(scene:SKScene) { 20 | self.scene = scene 21 | } 22 | 23 | open func initialize(_ size:CGFloat) { 24 | var n_follow = -1 25 | var textures3:[SKTexture]=[] 26 | if SkinBuffer.useSkin { 27 | var n = -1 28 | for i in 0...20 { 29 | _ = SkinBuffer.get("sliderb\(i)") 30 | if !SkinBuffer.getFlag("sliderb\(i)") { 31 | n = i - 1 32 | break 33 | } 34 | } 35 | //Parse following circle 36 | for i in 0...20 { 37 | _ = SkinBuffer.get("sliderfollowcircle-\(i)") 38 | if !SkinBuffer.getFlag("sliderfollowcircle-\(i)") { 39 | n_follow = i - 1 40 | break 41 | } 42 | } 43 | if n > -1 { 44 | debugPrint("sliderb count in skin: \(n+1)") 45 | useSkin = true 46 | sliderball1.colorBlendFactor = 0 47 | sliderball1.blendMode = .alpha 48 | sliderball1.zPosition=500000 49 | sliderball1.size=CGSize(width: size, height: size) 50 | sliderball1.alpha=0 51 | scene.addChild(self.sliderball1) 52 | if n > 0{ 53 | var textures1:[SKTexture]=[] 54 | for i in 0...n { 55 | textures1.append(SliderBall.sliderballimg("sliderb\(i)")) 56 | } 57 | sliderball1.run(.repeatForever(.animate(with: textures1, timePerFrame: 0.05))) 58 | } 59 | //Follow circle 60 | if n_follow > -1 { 61 | followcircle=SKSpriteNode(texture: SkinBuffer.get("sliderfollowcircle-0")) 62 | followcircle.size=CGSize(width: size*2, height: size*2) 63 | // followcircle.alpha=0 64 | followcircle.isHidden = true 65 | followcircle.zPosition=500000 66 | scene.addChild(followcircle) 67 | for i in 0...n_follow { 68 | textures3.append(SkinBuffer.get("sliderfollowcircle-\(i)")!) 69 | } 70 | followcircle.run(.repeatForever(.animate(with: textures3, timePerFrame: 0.2))) 71 | } else { 72 | followcircle.size=CGSize(width: size*2, height: size*2) 73 | // followcircle.alpha=0 74 | followcircle.isHidden = true 75 | followcircle.zPosition=500000 76 | scene.addChild(followcircle) 77 | } 78 | return 79 | } 80 | } 81 | sliderball1.zPosition=500000 82 | sliderball1.size=CGSize(width: size, height: size) 83 | sliderball1.alpha=0 84 | var textures1:[SKTexture]=[] 85 | for i in 0...9 { 86 | textures1.append(SliderBall.sliderballimg("sliderb\(i)")) 87 | } 88 | scene.addChild(self.sliderball1) 89 | sliderball1.run(.repeatForever(.animate(with: textures1, timePerFrame: 0.05))) 90 | //Follow circle 91 | if SkinBuffer.useSkin { 92 | if n_follow > -1 { 93 | followcircle=SKSpriteNode(texture: SkinBuffer.get("sliderfollowcircle-0")) 94 | followcircle.size=CGSize(width: size*2, height: size*2) 95 | // followcircle.alpha=0 96 | followcircle.isHidden = true 97 | followcircle.zPosition=500000 98 | scene.addChild(followcircle) 99 | for i in 0...n_follow { 100 | textures3.append(SkinBuffer.get("sliderfollowcircle-\(i)")!) 101 | } 102 | followcircle.run(.repeatForever(.animate(with: textures3, timePerFrame: 0.2))) 103 | return 104 | } 105 | } 106 | followcircle.size=CGSize(width: size*2, height: size*2) 107 | followcircle.isHidden = true 108 | followcircle.zPosition=500000 109 | scene.addChild(followcircle) 110 | } 111 | 112 | open func show(_ color:UIColor, path:UIBezierPath, repe:Int, duration:Double, waittime:Double) -> SKAction { 113 | return SKAction.sequence([.wait(forDuration: waittime),.run { 114 | let rpath=path.reversing() 115 | self.sliderball1.color = color 116 | var moving:[SKAction]=[SKAction.follow(path.cgPath, asOffset: false, orientToPath: true, duration: duration)] 117 | if repe>1 { 118 | for i in 2...repe { 119 | if(i%2==0){ 120 | moving.append(SKAction.follow(rpath.cgPath, asOffset: false, orientToPath: true, duration: duration)) 121 | }else{ 122 | moving.append(SKAction.follow(path.cgPath, asOffset: false, orientToPath: true, duration: duration)) 123 | } 124 | } 125 | } 126 | let action=SKAction.sequence([SKAction.fadeIn(withDuration:0),SKAction.sequence(moving),SKAction.fadeOut(withDuration: 0)]) 127 | self.sliderball1.run(action) 128 | self.followcircle.run(.sequence(moving), completion: { 129 | self.hidefollowcircle() 130 | self.followcircle.position = CGPoint(x: -99999, y: -99999) 131 | }) 132 | // self.followcircle.run(.sequence([.sequence(moving),.fadeOut(withDuration: 0.1)])) 133 | }]) 134 | } 135 | 136 | open func hideall() { 137 | sliderball1.alpha = 0 138 | hidefollowcircle() 139 | } 140 | 141 | open func showfollowcircle() { 142 | // followcircle.run(.fadeIn(withDuration: 0.1)) 143 | followcircle.isHidden = false 144 | } 145 | 146 | open func hidefollowcircle() { 147 | // followcircle.run(.fadeOut(withDuration: 0.1)) 148 | followcircle.isHidden = true 149 | } 150 | 151 | //In order to rotate images in SKAction 152 | fileprivate static func sliderballimg(_ file:String) -> SKTexture { 153 | let img=SkinBuffer.getimg(file) 154 | let rotatedViewBox=UIView(frame: CGRect(x: 0, y: 0, width: (img?.size.width)!, height: (img?.size.height)!)) 155 | rotatedViewBox.transform=CGAffineTransform(rotationAngle: -.pi/2) 156 | let rotatedSize=rotatedViewBox.frame.size 157 | UIGraphicsBeginImageContext(rotatedSize) 158 | let bitmap=UIGraphicsGetCurrentContext() 159 | bitmap?.translateBy(x: rotatedSize.width/2, y: rotatedSize.height/2) 160 | bitmap?.rotate(by: -.pi/2) 161 | bitmap?.scaleBy(x: 1.0, y: -1.0) 162 | bitmap?.draw((img?.cgImage)!, in: CGRect(x: -(img?.size.width)!/2, y: -(img?.size.height)!/2, width: (img?.size.width)!, height: (img?.size.height)!)) 163 | let newimg=UIGraphicsGetImageFromCurrentImageContext() 164 | UIGraphicsEndImageContext() 165 | return SKTexture(image: newimg!) 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /iosu/Beatmap/HitObjects/Spinner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Spinner.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Spinner:HitObject{ 12 | 13 | var endtime:Int 14 | 15 | init(time:Int,hitsound:Int,endtime:Int,newcombo:Bool) { 16 | self.endtime=endtime 17 | super.init(type: .spinner, x: 0, y: 0, time: time, hitsound: HitObject.hitsoundDecode(hitsound), newcombo: newcombo) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /iosu/Beatmap/Renderer/ActionSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSet.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class ActionSet { 13 | 14 | fileprivate var actions:[HitObjectAction]=[] 15 | fileprivate var actnums:[Int] = [] 16 | fileprivate var actcols:[UIColor] = [] 17 | fileprivate weak var scene:SKScene! 18 | fileprivate var nextindex:Int=0 19 | public static weak var difficulty:BMDifficulty? 20 | public static weak var current:ActionSet? 21 | 22 | func destroy() { 23 | for act in actions { 24 | act.destroy() 25 | } 26 | actions.removeAll() 27 | } 28 | 29 | init(beatmap:Beatmap,scene:SKScene) { 30 | ActionSet.difficulty = beatmap.difficulty 31 | self.scene=scene 32 | let colors=beatmap.colors 33 | var colorindex=0 34 | var number=0 35 | for obj in beatmap.hitobjects { 36 | switch obj.type { 37 | case .circle: 38 | if obj.newCombo { 39 | number=1 40 | colorindex+=1 41 | if colorindex == colors.count { 42 | colorindex = 0 43 | } 44 | } else { 45 | number+=1 46 | } 47 | actnums.append(number) 48 | actions.append(CircleAction(obj: obj as! HitCircle)) 49 | actcols.append(colors[colorindex]) 50 | break 51 | case .slider: 52 | if obj.newCombo { 53 | number=1 54 | colorindex+=1 55 | if colorindex == colors.count { 56 | colorindex = 0 57 | } 58 | } else { 59 | number+=1 60 | } 61 | actnums.append(number) 62 | actions.append(SliderAction(obj: obj as! Slider, timing: beatmap.getTimingPoint(obj.time))) 63 | actcols.append(colors[colorindex]) 64 | break 65 | case .spinner: 66 | break 67 | case .none: 68 | break 69 | } 70 | } 71 | ActionSet.current=self 72 | } 73 | 74 | open func prepare() { 75 | var layer:CGFloat = 100000 76 | for i in 0...actions.count-1 { 77 | actions[i].prepare(actcols[i], number: actnums[i], layer: layer) 78 | layer-=1 79 | } 80 | } 81 | 82 | open func hasnext() -> Bool { 83 | return nextindex= 0 { 89 | actions[nextindex].show(scene, offset: offset) 90 | } 91 | nextindex+=1 92 | } 93 | 94 | open func nexttime() -> Double { 95 | if nextindex < actions.count { 96 | return actions[nextindex].gettime() 97 | } 98 | return Double(Int.max) 99 | } 100 | 101 | open var pointer=0 102 | open func currentact() -> HitObjectAction? { 103 | if pointer SKTexture? { 25 | addtobuffer(file) 26 | if buffer[file] != nil { 27 | return buffer[file]! 28 | } 29 | return nil 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /iosu/Beatmap/Renderer/CircleAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleAction.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class CircleAction:HitObjectAction { 13 | 14 | fileprivate let time:Double 15 | fileprivate let obj:HitCircle 16 | fileprivate var inner:SKSpriteNode? = nil 17 | fileprivate var overlay:SKSpriteNode? = nil 18 | fileprivate var number:[SKSpriteNode]=[] 19 | fileprivate var appcircle:SKSpriteNode? = nil 20 | fileprivate var dummynode:SKNode? = nil 21 | fileprivate var guardnode:SKNode? = nil 22 | 23 | init(obj:HitCircle) { 24 | self.time=Double(obj.time) 25 | self.obj=obj 26 | } 27 | 28 | func prepare(_ color:UIColor,number:Int,layer:CGFloat) { 29 | let edge = CGFloat((ActionSet.difficulty?.AbsoluteCS)!) 30 | let size = CGSize(width: edge, height: edge) 31 | let position = CGPoint(x: obj.x, y: obj.y) 32 | var selflayer = layer 33 | //Draw inner 34 | selflayer += 0.1 35 | inner = SKSpriteNode(texture: SkinBuffer.get("hitcircle")) 36 | inner?.color = color 37 | inner?.blendMode = .alpha 38 | inner?.colorBlendFactor = 1 39 | inner?.size = size 40 | inner?.position = position 41 | inner?.zPosition = selflayer 42 | //Draw number 43 | selflayer += 0.1 44 | let lo = number % 10 45 | let lonode = CircleAction.num2node(lo) 46 | lonode.position = position 47 | lonode.zPosition = selflayer 48 | self.number.append(lonode) 49 | if number >= 10 { 50 | lonode.anchorPoint = CGPoint(x: 0.0, y: 0.5) 51 | selflayer += 0.1 52 | let hi = (number % 100) / 10 53 | let hinode = CircleAction.num2node(hi) 54 | hinode.anchorPoint = CGPoint(x: 1.0, y: 0.5) 55 | hinode.position = position 56 | hinode.zPosition = selflayer 57 | self.number.append(hinode) 58 | } 59 | //Draw overlay 60 | selflayer += 0.1 61 | overlay = SKSpriteNode(texture: SkinBuffer.get("hitcircleoverlay")) 62 | overlay?.colorBlendFactor = 0 63 | overlay?.size = size 64 | overlay?.position = position 65 | overlay?.zPosition = selflayer 66 | //Draw Approach Circle 67 | appcircle = SKSpriteNode(texture: SkinBuffer.get("approachcircle")) 68 | appcircle?.colorBlendFactor = 0 69 | appcircle?.size = size 70 | appcircle?.setScale(3) 71 | appcircle?.alpha = 0 72 | appcircle?.position = position 73 | appcircle?.zPosition = 100001 74 | } 75 | 76 | static func num2node(_ number:Int) -> SKSpriteNode { 77 | let texture = SkinBuffer.get("default-\(number)") 78 | var width = (texture?.size().width)! 79 | var height = (texture?.size().height)! 80 | let scale = CGFloat((ActionSet.difficulty?.AbsoluteCS)!) / 3 / height 81 | width *= scale 82 | height *= scale 83 | let node = SKSpriteNode(texture: texture) 84 | node.colorBlendFactor = 0 85 | node.size = CGSize(width: width, height: height) 86 | return node 87 | } 88 | 89 | static let faildisappear = SKAction.sequence([.fadeOut(withDuration: 0.1),.removeFromParent()]) 90 | func show(_ scene:SKScene,offset:Double) { 91 | let artime = (ActionSet.difficulty?.ARTime)!/1000 92 | let showact = SKAction.sequence([.wait(forDuration: offset/1000),.run{ 93 | scene.addChild(self.inner!) 94 | scene.addChild(self.overlay!) 95 | for num in self.number { 96 | scene.addChild(num) 97 | } 98 | scene.addChild(self.appcircle!) 99 | self.appcircle?.run(.sequence([.group([.fadeIn(withDuration: artime/3),.scale(to: 1, duration: artime)]),.removeFromParent()])) 100 | }]) 101 | let failact = SKAction.sequence([.wait(forDuration: artime+offset/1000+(ActionSet.difficulty?.Score50)!/1000),SKAction.playSoundFileNamed("combobreak.mp3", atVolume: GamePlayScene.effvolume, waitForCompletion: false),.run { 102 | ActionSet.current?.pointer+=1 103 | self.inner?.run(CircleAction.faildisappear) 104 | self.overlay?.run(CircleAction.faildisappear) 105 | for num in self.number { 106 | num.run(CircleAction.faildisappear) 107 | } 108 | //Show fail 109 | let img = SkinBuffer.get("hit0")! 110 | let node = SKSpriteNode(texture: img) 111 | let scale = CGFloat((ActionSet.difficulty?.AbsoluteCS)! / 128) 112 | node.setScale(scale) 113 | node.colorBlendFactor = 0 114 | node.alpha = 0 115 | node.position = CGPoint(x: self.obj.x, y: self.obj.y) 116 | node.zPosition = 100001 117 | scene.addChild(node) 118 | node.run(.group([.sequence([.fadeIn(withDuration: 0.2),.fadeOut(withDuration: 0.6),.removeFromParent()]),.sequence([.scale(by: 1.5, duration: 0.1),.scale(to: scale, duration: 0.1)])])) 119 | }]) 120 | dummynode = SKNode() 121 | scene.addChild(dummynode!) 122 | dummynode?.run(.group([showact,failact])) 123 | guardnode = SKNode() 124 | guardnode?.run(.wait(forDuration: artime+offset/1000+(ActionSet.difficulty?.Score50)!/1000+2), completion: { [weak self] in 125 | self?.destroy() 126 | }) 127 | } 128 | 129 | static let passdisappear = SKAction.sequence([.group([.fadeOut(withDuration: 0.2),.scale(to: 2, duration: 0.2),.removeFromParent()])]) 130 | //Time in ms 131 | func judge(_ time:Double) -> HitResult { 132 | ActionSet.current?.pointer+=1 133 | dummynode?.removeAllActions() 134 | dummynode?.run(.sequence([.wait(forDuration: 0.5),.removeFromParent()])) 135 | var d = time - self.time 136 | //debugPrint("d:\(d) score50:\((ActionSet.difficulty?.Score50)!)") 137 | if d < -(ActionSet.difficulty?.Score50)! { 138 | self.inner?.run(CircleAction.faildisappear) 139 | self.overlay?.run(CircleAction.faildisappear) 140 | for num in self.number { 141 | num.run(CircleAction.faildisappear) 142 | } 143 | self.appcircle?.run(CircleAction.faildisappear) 144 | return .fail 145 | } 146 | d = abs(d) 147 | if d <= (ActionSet.difficulty?.Score50)! { 148 | self.inner?.run(CircleAction.passdisappear) 149 | self.overlay?.run(CircleAction.passdisappear) 150 | for num in self.number { 151 | num.run(CircleAction.passdisappear) 152 | } 153 | self.appcircle?.removeFromParent() 154 | if d <= (ActionSet.difficulty?.Score300)! { 155 | return .s300 156 | } 157 | if d <= (ActionSet.difficulty?.Score100)! { 158 | return .s100 159 | } 160 | return .s50 161 | } 162 | self.inner?.run(CircleAction.faildisappear) 163 | self.overlay?.run(CircleAction.faildisappear) 164 | for num in self.number { 165 | num.run(CircleAction.faildisappear) 166 | } 167 | self.appcircle?.run(CircleAction.faildisappear) 168 | return .fail 169 | } 170 | 171 | func gettime() -> Double { 172 | return time 173 | } 174 | 175 | func getobj() -> HitObject { 176 | return obj 177 | } 178 | 179 | func destroy() { 180 | inner?.removeFromParent() 181 | inner = nil 182 | for node in number { 183 | node.removeFromParent() 184 | } 185 | number.removeAll() 186 | overlay?.removeFromParent() 187 | overlay = nil 188 | appcircle?.removeFromParent() 189 | appcircle = nil 190 | dummynode?.removeFromParent() 191 | dummynode = nil 192 | guardnode?.removeFromParent() 193 | guardnode = nil 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /iosu/Beatmap/Renderer/HitObjectAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HitObjectAction.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | protocol HitObjectAction { 13 | func prepare(_ color:UIColor,number:Int,layer:CGFloat) 14 | //Time in ms 15 | func gettime() -> Double 16 | func show(_ scene:SKScene,offset:Double) 17 | func getobj() -> HitObject 18 | func destroy() 19 | } 20 | -------------------------------------------------------------------------------- /iosu/Beatmap/Renderer/RendererEnums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RendererEnums.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SliderStatus { 12 | case head 13 | case arrow 14 | case end 15 | } 16 | 17 | enum SliderFeedback { 18 | case nothing 19 | case edgePass 20 | case tickPass 21 | case failOnce 22 | case failTick 23 | case failAll 24 | case end 25 | } 26 | -------------------------------------------------------------------------------- /iosu/Beatmap/Renderer/SkinBuffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SkinHelper.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/6/29. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class SkinBuffer { 13 | 14 | public static var useSkin = true 15 | public static var bmPath = "" 16 | 17 | fileprivate static var buffer=[String:SKTexture]() 18 | fileprivate static var skinflag=[String:Bool]() 19 | 20 | fileprivate static func add(_ file: String) { 21 | if buffer[file] != nil { 22 | return 23 | } 24 | debugPrint("finding \(file)") 25 | if useSkin { 26 | let image=UIImage(contentsOfFile: (bmPath as NSString).appendingPathComponent(file.appending(".png"))) 27 | if image != nil { 28 | debugPrint("\((bmPath as NSString).appendingPathComponent(file.appending(".png")))") 29 | let texture=SKTexture(image: image!) 30 | buffer[file]=texture 31 | skinflag[file]=true 32 | return 33 | } 34 | } 35 | let texture=SKTexture(imageNamed: file) 36 | buffer[file]=texture 37 | skinflag[file]=false 38 | } 39 | 40 | public static func get(_ file: String) -> SKTexture? { 41 | add(file) 42 | if buffer[file] != nil { 43 | return buffer[file]! 44 | } 45 | return nil 46 | } 47 | 48 | public static func getimg(_ file: String) -> UIImage? { 49 | add(file) 50 | if buffer[file] != nil { 51 | return UIImage(cgImage: (buffer[file]?.cgImage())!) 52 | } 53 | return nil 54 | } 55 | 56 | public static func getFlag(_ file: String) -> Bool { 57 | return skinflag[file]! 58 | } 59 | 60 | public static func clean() { 61 | buffer.removeAll() 62 | skinflag.removeAll() 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /iosu/Beatmap/Scoring/ScoringEnums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScoringEnums.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum HitResult { 12 | case s300 13 | case s100 14 | case s50 15 | case fail 16 | } 17 | -------------------------------------------------------------------------------- /iosu/Beatmap/TimingPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimingPoint.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TimingPoint { 12 | 13 | var offset:Int 14 | var timeperbeat:Double //In milliseconds 15 | var beattype:Int //3:3/3,4:4/4 16 | var sampleset:SampleSet 17 | var samplesetid:Int //0 for default 18 | var volume:Int //0~100 19 | var inherited:Bool 20 | var kiai:Bool //Climax of the song 21 | 22 | init(offset:Int,timeperbeat:Double,beattype:Int,sampleset:SampleSet,samplesetid:Int,volume:Int,inherited:Bool,kiai:Bool) { 23 | self.offset=offset 24 | self.timeperbeat=timeperbeat 25 | self.beattype=beattype 26 | self.sampleset=sampleset 27 | self.samplesetid=samplesetid 28 | self.volume=volume 29 | self.inherited=inherited 30 | self.kiai=kiai 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /iosu/Database/BeatmapScanner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BeatmapProcessor.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/3/30. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BeatmapScanner{ 12 | 13 | //var docPath:NSString 14 | open var beatmapdirs:[String]=[] 15 | open var bmdirurls:[URL]=[] 16 | open var beatmaps:[String]=[] 17 | open var storyboards=[String:String]() 18 | open var dirscontainsb:[String]=[] 19 | 20 | init() { 21 | //let home=NSHomeDirectory() as NSString 22 | //docPath=home.strings(byAppendingPaths: "Documents") 23 | let manager=FileManager.default 24 | let docURL=manager.urls(for: .documentDirectory, in: .userDomainMask) 25 | let url=docURL[0] as URL 26 | let contentsOfPath=try? manager.contentsOfDirectory(atPath: url.path) 27 | for entry in contentsOfPath!{ 28 | //debugPrint("folder:\(entry)") 29 | let contentsOfBMPath=try? manager.contentsOfDirectory(atPath: url.appendingPathComponent(entry).path) 30 | if contentsOfBMPath == nil { 31 | continue 32 | } 33 | for subentry in contentsOfBMPath!{ 34 | //debugPrint(" \(subentry)") 35 | if subentry.hasSuffix(".osu"){ 36 | let fullpath=url.appendingPathComponent(entry, isDirectory: true) 37 | beatmapdirs.append(fullpath.path) 38 | bmdirurls.append(fullpath) 39 | //fullpath=fullpath.appendingPathComponent(subentry, isDirectory: false) 40 | //beatmaps.append(fullpath.path) 41 | beatmaps.append(subentry) 42 | } 43 | if subentry.hasSuffix(".osb"){ 44 | let fullpath=url.appendingPathComponent(entry, isDirectory: true) 45 | //beatmapdirs.append(fullpath.path) 46 | //fullpath=fullpath.appendingPathComponent(subentry, isDirectory: false) 47 | //beatmaps.append(fullpath.path) 48 | dirscontainsb.append(fullpath.path) 49 | storyboards.updateValue(subentry, forKey: fullpath.path) 50 | } 51 | } 52 | } 53 | } 54 | 55 | func count()->Int{ 56 | return beatmaps.count 57 | } 58 | 59 | func get(index:Int)->String{ 60 | return beatmaps[index] 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /iosu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | iosu! 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.3.1 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIFileSharingEnabled 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UIRequiresFullScreen 36 | 37 | UIStatusBarHidden 38 | 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /iosu/MediaPlayers/BGMusicPlayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MusicPlayer.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/3/30. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AVFoundation 11 | import SpriteKit 12 | import QuartzCore 13 | 14 | enum BGMusicState { 15 | case playing 16 | case paused 17 | case stopped 18 | } 19 | 20 | class BGMusicPlayer: NSObject, AVAudioPlayerDelegate { 21 | 22 | static let instance = BGMusicPlayer() 23 | 24 | fileprivate var musicPlayer: AVAudioPlayer! 25 | open var gameEarliest:Int = 0 26 | open var videoEarliest:Int = 0 27 | open var sbEarliest:Int = 0 28 | open weak var gameScene:GamePlayScene? 29 | open weak var sbScene:StoryBoardScene? 30 | open var bgmvolume:Float = 1.0 31 | open var state: BGMusicState = .stopped 32 | 33 | func setfile(_ file:String) { 34 | let url=URL(fileURLWithPath: file) 35 | self.musicPlayer = try! AVAudioPlayer(contentsOf: url) 36 | self.musicPlayer.numberOfLoops=0 37 | self.musicPlayer.volume = bgmvolume 38 | self.musicPlayer.delegate = self 39 | state = .paused 40 | } 41 | 42 | fileprivate var starttime:Double = 0 43 | 44 | func startPlaying() { 45 | debugPrint("game earliest: \(gameEarliest)") 46 | debugPrint("video earliest: \(videoEarliest)") 47 | debugPrint("sb earliest: \(sbEarliest)") 48 | var offset = -min(gameEarliest,videoEarliest,sbEarliest) 49 | debugPrint("music offset: \(offset)") 50 | if offset < 3000 { 51 | offset = 3000 52 | } else { 53 | offset += 100 54 | } 55 | starttime = CACurrentMediaTime() + Double(offset)/1000 56 | let musicnode = SKNode() 57 | if gameScene != nil { 58 | gameScene?.addChild(musicnode) 59 | } else { 60 | sbScene?.addChild(musicnode) 61 | } 62 | musicnode.run(SKAction.sequence([SKAction.wait(forDuration: Double(offset)/1000), SKAction.run { 63 | self.musicPlayer.prepareToPlay() 64 | self.musicPlayer.play() 65 | self.state = .playing 66 | self.starttime = CACurrentMediaTime() 67 | }])) 68 | } 69 | 70 | func getTime() -> TimeInterval{ 71 | return CACurrentMediaTime() - starttime 72 | } 73 | 74 | func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { 75 | state = .stopped 76 | } 77 | 78 | func pause() { 79 | if musicPlayer.isPlaying { 80 | musicPlayer.pause() 81 | } 82 | state = .paused 83 | } 84 | 85 | func play() { 86 | if !musicPlayer.isPlaying { 87 | musicPlayer.play() 88 | } 89 | state = .playing 90 | } 91 | 92 | func stop() { 93 | musicPlayer.stop() 94 | state = .stopped 95 | } 96 | 97 | func isplaying() -> Bool{ 98 | if musicPlayer==nil { 99 | return false 100 | } 101 | return musicPlayer.isPlaying 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /iosu/MediaPlayers/BGVideoPlayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BGVideoPlayer.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/4/11. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class BGVPlayer : NSObject, VLCMediaPlayerDelegate { 13 | 14 | //static var vplayer=KSYMoviePlayerController(contentURL: URL(fileURLWithPath: "")) 15 | static var view:UIView? 16 | static var mplayer = VLCMediaPlayer() 17 | 18 | static func myInitialize() { 19 | view = UIView() 20 | view?.backgroundColor = .clear 21 | view?.frame = UIScreen.screens[0].bounds 22 | mplayer.drawable = view 23 | mplayer.audio.isMuted = true 24 | view?.isHidden = true 25 | } 26 | 27 | static func setcontent(_ file:String) -> SKAction { 28 | return SKAction.run { 29 | let media = VLCMedia(path: file) 30 | mplayer.media = media 31 | let notification=NotificationCenter.default 32 | let operationQueue=OperationQueue.main 33 | _ = notification.addObserver(forName: NSNotification.Name(rawValue: VLCMediaPlayerStateChanged), object: nil, queue: operationQueue, using: {(notif) in 34 | //Stopped 35 | if mplayer.state.rawValue == 0 { 36 | mplayer.media = nil 37 | view?.isHidden = true 38 | } 39 | }) 40 | } 41 | } 42 | 43 | static func play() -> SKAction { 44 | return SKAction.run { 45 | mplayer.play() 46 | view?.isHidden = false 47 | } 48 | } 49 | 50 | static func pause() { 51 | if mplayer.isPlaying { 52 | mplayer.pause() 53 | } 54 | } 55 | 56 | static func play() { 57 | if !mplayer.isPlaying { 58 | mplayer.play() 59 | } 60 | } 61 | 62 | static func stop() { 63 | mplayer.stop() 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /iosu/MediaPlayers/BundleAudioBuffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BundleAudioBuffer.swift 3 | // iosu 4 | // 5 | // Created by 谢宜 on 2017/6/21. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BundleAudioBuffer{ 12 | 13 | static var buffer=[String:Data]() 14 | 15 | static func addtobuffer(_ file:String) { 16 | if buffer[file] != nil { 17 | return 18 | } 19 | 20 | let nameOnly = (file as NSString).deletingPathExtension 21 | let fileExt = (file as NSString).pathExtension 22 | 23 | let soundPath = Bundle.main.url(forResource: nameOnly, withExtension: fileExt) 24 | let audio = try! Data(contentsOf: soundPath!) 25 | buffer[file]=audio as Data? 26 | } 27 | 28 | static func get(_ file:String) -> Data? { 29 | addtobuffer(file) 30 | if buffer[file] != nil { 31 | return buffer[file]! 32 | } 33 | return nil 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /iosu/MediaPlayers/PlaySoundFileNamedExt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaySoundFileNamedExt.swift 3 | // iosu 4 | // 5 | // Created by 谢宜 on 2017/6/21. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | // Reference: https://gist.github.com/ainoya/a2c1cd026ad5d695e406 9 | import SpriteKit 10 | import AVFoundation 11 | 12 | //In order to fix EXC_BAD_ADDRESS exception 13 | class SoundNode:SKNode { 14 | 15 | fileprivate let player:AVAudioPlayer 16 | 17 | init(player: AVAudioPlayer) { 18 | self.player = player 19 | super.init() 20 | } 21 | 22 | required init?(coder aDecoder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | open func play() { 27 | self.run(SKAction.sequence([SKAction.run{ 28 | self.player.play() 29 | },SKAction.wait(forDuration: player.duration),SKAction.run { 30 | self.player.stop() 31 | self.removeFromParent() 32 | }])) 33 | } 34 | 35 | } 36 | 37 | public extension SKAction { 38 | 39 | public class func playSoundFileNamed(_ fileName: String, atVolume: Float, waitForCompletion: Bool) -> SKAction { 40 | do { 41 | let player = try AVAudioPlayer(data: BundleAudioBuffer.get(fileName)!) 42 | let dummynode = SoundNode(player: player) 43 | GamePlayScene.current?.addChild(dummynode) 44 | player.volume = atVolume 45 | player.prepareToPlay() 46 | let playAction = SKAction.run { 47 | dummynode.play() 48 | } 49 | if(waitForCompletion){ 50 | let waitAction = SKAction.wait(forDuration: player.duration) 51 | let groupAction: SKAction = SKAction.group([playAction, waitAction]) 52 | return groupAction 53 | } 54 | return playAction 55 | } catch let error { 56 | debugPrint("\(fileName) caused: \(error.localizedDescription)") 57 | } 58 | return .run{} 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /iosu/Scenes/TestScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestScene.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/4/7. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import SpriteKit 12 | 13 | class TestScene:SKScene { 14 | 15 | let spinner_appcircle = SKSpriteNode(imageNamed: "spinner-approachcircle") 16 | let spinner_bottom = SKSpriteNode(imageNamed: "spinner-bottom") 17 | let spinner_clear = SKSpriteNode(imageNamed: "spinner-clear") 18 | let spinner_glow = SKSpriteNode(imageNamed: "spinner-glow") 19 | let spinner_middle = SKSpriteNode(imageNamed: "spinner-middle") 20 | let spinner_middle2 = SKSpriteNode(imageNamed: "spinner-middle2") 21 | let spinner_spin = SKSpriteNode(imageNamed: "spinner-spin") 22 | let spinner_top = SKSpriteNode(imageNamed: "spinner-top") 23 | 24 | var scrscale:CGFloat { 25 | return size.height/480 26 | } 27 | var realwidth:CGFloat { 28 | return 512*scrscale 29 | } 30 | var realheight:CGFloat { 31 | return 384*scrscale 32 | } 33 | var bottomedge:CGFloat { 34 | return (size.height - realheight)/2 35 | } 36 | var leftedge:CGFloat { 37 | return (size.width - realwidth)/2 38 | } 39 | var center:CGPoint { 40 | return CGPoint(x: leftedge + realwidth/2, y: bottomedge + realheight/2) 41 | } 42 | 43 | override func sceneDidLoad() { 44 | spinner_glow.size = CGSize(width: realheight*0.85, height: realheight*0.85) 45 | spinner_glow.position = center 46 | spinner_glow.zPosition = 0 47 | addChild(spinner_glow) 48 | spinner_bottom.size = CGSize(width: realheight*2/3, height: realheight*2/3) 49 | spinner_bottom.position = center 50 | spinner_bottom.zPosition = 1 51 | addChild(spinner_bottom) 52 | spinner_top.size = CGSize(width: realheight*2/3, height: realheight*2/3) 53 | spinner_top.position = center 54 | spinner_top.zPosition = 2 55 | addChild(spinner_top) 56 | spinner_middle2.size = CGSize(width: realheight/50, height: realheight/50) 57 | spinner_middle2.position = center 58 | spinner_middle2.zPosition = 3 59 | addChild(spinner_middle2) 60 | spinner_middle.size = CGSize(width: realheight*2/3*1.012, height: realheight*2/3*1.012) 61 | spinner_middle.position = center 62 | spinner_middle.zPosition = 4 63 | addChild(spinner_middle) 64 | spinner_spin.setScale(realheight/800) 65 | spinner_spin.position = CGPoint(x: leftedge + realwidth/2, y: bottomedge + realheight/3.6) 66 | spinner_spin.zPosition = 5 67 | addChild(spinner_spin) 68 | } 69 | 70 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 71 | debugPrint("touch") 72 | } 73 | 74 | var firstrun=true 75 | 76 | override func update(_ currentTime: TimeInterval) { 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /iosu/StoryBoard/MovingImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovingImage.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class MovingImage:BasicImage { 13 | 14 | var framecount:Int 15 | var framedelay:Double 16 | var looptype:LoopType 17 | var paths:[String]=[] 18 | 19 | init(layer:SBLayer,rlayer:Double,origin:SBOrigin,filepath:String,x:Double,y:Double,framecount:Int,framedelay:Double,looptype:LoopType) { 20 | self.framecount=framecount 21 | self.framedelay=framedelay 22 | self.looptype=looptype 23 | super.init(layer: layer, rlayer: rlayer, origin: origin, filepath: filepath, x: x, y: y,isanimation:true) 24 | } 25 | 26 | override func convertsprite() { 27 | let ext=filepath.components(separatedBy: ".").last 28 | let extlen=ext?.lengthOfBytes(using: .utf8) 29 | for i in 0...framecount-1 { 30 | var path=filepath.substring(to:filepath.index(filepath.endIndex, offsetBy: -(extlen!+1))) 31 | path+="\(i)."+ext! 32 | paths.append(path) 33 | } 34 | filepath=paths.first! 35 | super.convertsprite() 36 | } 37 | 38 | func animate(){ 39 | var textures:[SKTexture]=[] 40 | for path in paths { 41 | let image=ImageBuffer.get(path) 42 | if(image==nil){ 43 | continue 44 | } 45 | textures.append(image!) 46 | } 47 | if(textures.count==0){ 48 | return 49 | } 50 | var animateaction=SKAction.animate(with: textures, timePerFrame: framedelay/1000) 51 | switch looptype { 52 | case .loopForever: 53 | animateaction=SKAction.repeatForever(animateaction) 54 | case .loopOnce: 55 | break 56 | } 57 | actions=SKAction.group([animateaction,actions!]) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBColor.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBColor:SBCommand,SBCAction { 14 | 15 | var startr:Double 16 | var startg:Double 17 | var startb:Double 18 | var endr:Double 19 | var endg:Double 20 | var endb:Double 21 | 22 | init(easing:Int,starttime:Int,endtime:Int,startr:Double,startg:Double,startb:Double,endr:Double,endg:Double,endb:Double) { 23 | self.startr=startr/255 24 | self.startg=startg/255 25 | self.startb=startb/255 26 | self.endr=endr/255 27 | self.endg=endg/255 28 | self.endb=endb/255 29 | super.init(type: .color, easing: easing, starttime: starttime, endtime: endtime) 30 | } 31 | 32 | func toAction() -> SKAction { 33 | if starttime==endtime { 34 | return SKAction.colorize(with: UIColor(red: CGFloat(endr), green: CGFloat(endg), blue: CGFloat(endb), alpha: 1), colorBlendFactor: 1, duration: 0) 35 | } 36 | return SKEase.color(easeFunction: easing.function, easeType: easing.type, time: duration, rfrom: CGFloat(startr), gfrom: CGFloat(startg), bfrom: CGFloat(startb), rto: CGFloat(endr), gto: CGFloat(endg), bto: CGFloat(endb)) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBFade.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBFade.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBFade:SBCommand,SBCAction { 14 | 15 | var startopacity:Double 16 | var endopacity:Double 17 | 18 | init(easing:Int,starttime:Int,endtime:Int,startopacity:Double,endopacity:Double) { 19 | self.startopacity=startopacity 20 | self.endopacity=endopacity 21 | super.init(type: .fade, easing: easing, starttime: starttime, endtime: endtime) 22 | } 23 | 24 | func toAction() -> SKAction { 25 | if starttime==endtime{ 26 | //debugPrint("set alpha: \(endopacity)") 27 | return SKAction.fadeAlpha(to: CGFloat(endopacity), duration: 0) 28 | } 29 | if startopacity==endopacity { 30 | return SKAction.sequence([SKAction.fadeAlpha(to: CGFloat(endopacity), duration: 0),SKAction.wait(forDuration: duration)]) 31 | } 32 | return SKEase.fade(easeFunction: easing.function, easeType: easing.type, time: duration, fromValue: CGFloat(startopacity), toValue: CGFloat(endopacity)) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBLoop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBLoop.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | //Compound Commands 14 | //https://osu.ppy.sh/wiki/Storyboard_Triggers 15 | class SBLoop:SBCommand,SBCAction { 16 | 17 | var loopcount:Int 18 | var commands:[SBCommand]=[] 19 | 20 | init(starttime:Int,loopcount:Int) { 21 | self.loopcount=loopcount 22 | super.init(type: .loop, easing: 0, starttime: starttime, endtime: 0) 23 | } 24 | 25 | func genendtime() { 26 | var latest:Int = .min 27 | for cmd in commands { 28 | if(latest SKAction { 42 | //return SKAction.repeat(SKAction.group(commands), count: loopcount) 43 | var loopactions:[SKAction]=[] 44 | for cmd in commands { 45 | //cmd.sprite=self.sprite 46 | loopactions.append(SKAction.sequence([SKAction.wait(forDuration: Double(cmd.starttime)/1000),(cmd as! SBCAction).toAction()])) 47 | } 48 | return SKAction.repeat(SKAction.group(loopactions), count: self.loopcount) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBMove.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBMove.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBMove:SBCommand,SBCAction { 14 | 15 | var startx:Double 16 | var starty:Double 17 | var endx:Double 18 | var endy:Double 19 | 20 | init(easing:Int,starttime:Int,endtime:Int,startx:Double,starty:Double,endx:Double,endy:Double) { 21 | self.startx=StoryBoard.conv(x: startx) 22 | self.starty=StoryBoard.conv(y: starty) 23 | self.endx=StoryBoard.conv(x: endx) 24 | self.endy=StoryBoard.conv(y: endy) 25 | super.init(type: .move, easing: easing, starttime: starttime, endtime: endtime) 26 | } 27 | 28 | func toAction() -> SKAction { 29 | let from=CGPoint(x: startx, y: starty) 30 | let to=CGPoint(x: endx, y: endy) 31 | if starttime==endtime { 32 | return SKAction.move(to: to, duration: 0) 33 | } 34 | return SKEase.move(easeFunction: easing.function, easeType: easing.type, time: duration, from: from, to: to) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBMoveX.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBMoveX.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBMoveX:SBCommand,SBCAction { 14 | 15 | var startx:Double 16 | var endx:Double 17 | 18 | init(easing:Int,starttime:Int,endtime:Int,startx:Double,endx:Double) { 19 | self.startx=StoryBoard.conv(x:startx) 20 | self.endx=StoryBoard.conv(x:endx) 21 | super.init(type: .moveX, easing: easing, starttime: starttime, endtime: endtime) 22 | } 23 | 24 | func toAction() -> SKAction { 25 | if starttime==endtime { 26 | return SKAction.moveTo(x: CGFloat(endx), duration: 0) 27 | } 28 | return SKEase.moveX(easeFunction: easing.function, easeType: easing.type, time: duration, from: CGFloat(startx), to: CGFloat(endx)) 29 | /*return SKAction.customAction(withDuration: duration, actionBlock: { (node:SKNode, elapsedTime:CGFloat) -> Void in 30 | let from=CGPoint(x: CGFloat(self.startx), y: node.position.y) 31 | let to=CGPoint(x: CGFloat(self.endx), y: node.position.y) 32 | node.run(SKEase.move(easeFunction: self.easing.function, easeType: self.easing.type, time: self.duration, from: from, to: to)) 33 | })*/ 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBMoveY.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBMoveY.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBMoveY:SBCommand,SBCAction { 14 | 15 | var starty:Double 16 | var endy:Double 17 | 18 | init(easing:Int,starttime:Int,endtime:Int,starty:Double,endy:Double) { 19 | self.starty=StoryBoard.conv(y:starty) 20 | self.endy=StoryBoard.conv(y:endy) 21 | super.init(type: .moveY, easing: easing, starttime: starttime, endtime: endtime) 22 | } 23 | 24 | func toAction() -> SKAction { 25 | if starttime==endtime { 26 | return SKAction.moveTo(y: CGFloat(endy), duration: 0) 27 | } 28 | return SKEase.moveY(easeFunction: easing.function, easeType: easing.type, time: duration, from: CGFloat(starty), to: CGFloat(endy)) 29 | /*return SKAction.customAction(withDuration: duration, actionBlock: { (node:SKNode, elapsedTime:CGFloat) -> Void in 30 | let from=CGPoint(x: node.position.x, y: CGFloat(self.starty)) 31 | let to=CGPoint(x: node.position.x, y: CGFloat(self.endy)) 32 | node.run(SKEase.move(easeFunction: self.easing.function, easeType: self.easing.type, time: self.duration, from: from, to: to)) 33 | })*/ 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBParam.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBParam.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | enum SBParameterType { 14 | case h //Flip image horizontally 15 | case v //Flip image vertically 16 | case a //Use additive-color blending instead of alpha-blending 17 | case n //Unknown type 18 | } 19 | 20 | class SBParam:SBCommand,SBCAction { 21 | 22 | var paramtype:SBParameterType 23 | 24 | init(easing:Int,starttime:Int,endtime:Int,ptype:String) { 25 | //debugPrint("typestr: \((ptype as NSString).substring(to: 1))") 26 | if ptype.contains("H"){ 27 | self.paramtype = .h 28 | } else if ptype.contains("V"){ 29 | self.paramtype = .v 30 | } else if ptype.contains("A"){ 31 | self.paramtype = .a 32 | } else { 33 | self.paramtype = .n 34 | } 35 | super.init(type: .parameter, easing: easing, starttime: starttime, endtime: endtime) 36 | } 37 | 38 | func toAction() -> SKAction { 39 | //debugPrint("convert parameter to action, type \(self.paramtype)") 40 | switch paramtype { 41 | case .h: 42 | let flip={(node:SKNode,time:CGFloat)->Void in 43 | (node as! FlipNode).hflip=true 44 | } 45 | if starttime==endtime { 46 | return SKAction.customAction(withDuration: 0, actionBlock: flip) 47 | } 48 | let restore={(node:SKNode,time:CGFloat)->Void in 49 | (node as! FlipNode).hflip=false 50 | } 51 | return SKAction.sequence([SKAction.customAction(withDuration: 0, actionBlock: flip),SKAction.wait(forDuration: duration),SKAction.customAction(withDuration: 0, actionBlock: restore)]) 52 | case .v: 53 | let flip={(node:SKNode,time:CGFloat)->Void in 54 | (node as! FlipNode).vflip=true 55 | } 56 | if starttime==endtime { 57 | return SKAction.customAction(withDuration: 0, actionBlock: flip) 58 | } 59 | let restore={(node:SKNode,time:CGFloat)->Void in 60 | (node as! FlipNode).vflip=false 61 | } 62 | return SKAction.sequence([SKAction.customAction(withDuration: 0, actionBlock: flip),SKAction.wait(forDuration: duration),SKAction.customAction(withDuration: 0, actionBlock: restore)]) 63 | case .a: 64 | let set={(node:SKNode,time:CGFloat)->Void in 65 | (node as! SKSpriteNode).blendMode = .add 66 | } 67 | if(starttime==endtime){ 68 | return SKAction.customAction(withDuration: 0, actionBlock: set) 69 | } 70 | let restore={(node:SKNode,time:CGFloat)->Void in 71 | (node as! SKSpriteNode).blendMode = .alpha 72 | } 73 | return SKAction.sequence([SKAction.customAction(withDuration: 0, actionBlock: set),SKAction.wait(forDuration: duration),SKAction.customAction(withDuration: 0, actionBlock: restore)]) 74 | case .n: 75 | return SKAction.run { 76 | } 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBRotate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBRotate.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBRotate:SBCommand,SBCAction { 14 | 15 | //Both in radians 16 | var startr:Double 17 | var endr:Double 18 | 19 | init(easing:Int,starttime:Int,endtime:Int,startr:Double,endr:Double) { 20 | self.startr = -startr 21 | self.endr = -endr 22 | super.init(type: .rotate, easing: easing, starttime: starttime, endtime: endtime) 23 | } 24 | 25 | func toAction() -> SKAction { 26 | if starttime==endtime { 27 | return SKAction.rotate(toAngle: CGFloat(endr), duration: 0) 28 | } 29 | return SKEase.rotate(easeFunction: easing.function, easeType: easing.type, time: duration, from: CGFloat(startr), to: CGFloat(endr)) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBScale.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBScale.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBScale:SBCommand,SBCAction { 14 | 15 | var starts:Double 16 | var ends:Double 17 | 18 | init(easing:Int,starttime:Int,endtime:Int,starts:Double,ends:Double) { 19 | self.starts=starts 20 | self.ends=ends 21 | super.init(type: .scale, easing: easing, starttime: starttime, endtime: endtime) 22 | } 23 | 24 | func toAction() -> SKAction { 25 | if starttime==endtime { 26 | return SKAction.scale(to: CGFloat(ends), duration: 0) 27 | } 28 | return SKEase.scale(easeFunction: easing.function, easeType: easing.type, time: duration, from: CGFloat(starts), to: CGFloat(ends)) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBTrigger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBTrigger.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBTrigger:SBCommand,SBCAction { 14 | 15 | //TODO: Trigger Command 16 | 17 | init(easing:Int,starttime:Int,endtime:Int) { 18 | super.init(type: .trigger, easing: easing, starttime: starttime, endtime: endtime) 19 | } 20 | 21 | func toAction() -> SKAction { 22 | return SKAction.group([]) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Commands/SBVScale.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBVScale.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | import SpriteKitEasingSwift 12 | 13 | class SBVScale:SBCommand,SBCAction { 14 | 15 | var startsx:Double 16 | var startsy:Double 17 | var endsx:Double 18 | var endsy:Double 19 | 20 | init(easing:Int,starttime:Int,endtime:Int,startsx:Double,startsy:Double,endsx:Double,endsy:Double) { 21 | self.startsx=startsx 22 | self.startsy=startsy 23 | self.endsx=endsx 24 | self.endsy=endsy 25 | super.init(type: .vScale, easing: easing, starttime: starttime, endtime: endtime) 26 | } 27 | 28 | func toAction() -> SKAction { 29 | if starttime==endtime { 30 | return SKAction.group([SKAction.scaleX(to: CGFloat(endsx), duration: 0),SKAction.scaleY(to: CGFloat(endsy), duration: 0)]) 31 | } 32 | return SKEase.vscale(easeFunction: easing.function, easeType: easing.type, time: duration, xfrom: CGFloat(startsx), yfrom: CGFloat(startsy), xto: CGFloat(endsx), yto: CGFloat(endsy)) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/Easing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Easing.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKitEasingSwift 11 | 12 | class Easing { 13 | 14 | var function:CurveType 15 | var type:EaseType 16 | 17 | init(function:CurveType,type:EaseType) { 18 | self.function=function 19 | self.type=type 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/ImageBuffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageBuffer.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | //Significantly optimize memory usage and framerate 13 | class ImageBuffer{ 14 | 15 | static var buffer=[String:SKTexture]() 16 | static var notfoundimages=Set() 17 | 18 | static func addtobuffer(_ file:String) { 19 | if buffer[file] != nil { 20 | return 21 | } 22 | var image=UIImage(contentsOfFile: file) 23 | if image==nil { 24 | debugPrint("image not found: \(file)") 25 | debugPrint("Trying to fix") 26 | image=UIImage(contentsOfFile: file.replacingOccurrences(of: "/sb/", with: "/SB/")) 27 | if image==nil { 28 | image=UIImage(contentsOfFile: file.replacingOccurrences(of: "/SB/", with: "/sb/")) 29 | if image==nil { 30 | debugPrint("Failed.") 31 | notfoundimages.insert(file) 32 | return 33 | } 34 | } 35 | } 36 | let texture=SKTexture(image: image!) 37 | buffer[file]=texture 38 | } 39 | 40 | static func notfound2str() -> String { 41 | var str="Cannot find following images:\n" 42 | for line in notfoundimages { 43 | str+=line+"\n" 44 | } 45 | return str 46 | } 47 | 48 | static func get(_ file:String) ->SKTexture? { 49 | addtobuffer(file) 50 | if buffer[file] != nil { 51 | return buffer[file]! 52 | } 53 | return nil 54 | //return SKTexture() 55 | } 56 | 57 | static func clean() { 58 | buffer.removeAll() 59 | notfoundimages.removeAll() 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/SBCHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBCHelper.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class SBCHelper { 12 | 13 | static func str2cmdtype(_ string:String) -> StoryBoardCommand { 14 | var str=string 15 | while str.hasPrefix(" ") || str.hasPrefix("_") { 16 | str=(str as NSString).substring(from: 1) 17 | } 18 | switch str { 19 | case "F":return StoryBoardCommand.fade 20 | case "M":return StoryBoardCommand.move 21 | case "MX":return StoryBoardCommand.moveX 22 | case "MY":return StoryBoardCommand.moveY 23 | case "S":return StoryBoardCommand.scale 24 | case "V":return StoryBoardCommand.vScale 25 | case "R":return StoryBoardCommand.rotate 26 | case "C":return StoryBoardCommand.color 27 | case "P":return StoryBoardCommand.parameter 28 | case "L":return StoryBoardCommand.loop 29 | case "T":return StoryBoardCommand.trigger 30 | default:return StoryBoardCommand.unknown 31 | } 32 | } 33 | 34 | static func num2easing(_ num:Int) -> Easing { 35 | switch num { 36 | case 0:return Easing(function: .curveTypeLinear, type: .easeTypeIn) 37 | case 1:return Easing(function: .curveTypeSine, type: .easeTypeOut) 38 | case 2:return Easing(function: .curveTypeSine, type: .easeTypeIn) 39 | case 3:return Easing(function: .curveTypeQuadratic, type: .easeTypeIn) 40 | case 4:return Easing(function: .curveTypeQuadratic, type: .easeTypeOut) 41 | case 5:return Easing(function: .curveTypeQuadratic, type: .easeTypeInOut) 42 | case 6:return Easing(function: .curveTypeCubic, type: .easeTypeIn) 43 | case 7:return Easing(function: .curveTypeCubic, type: .easeTypeOut) 44 | case 8:return Easing(function: .curveTypeCubic, type: .easeTypeInOut) 45 | case 9:return Easing(function: .curveTypeQuartic, type: .easeTypeIn) 46 | case 10:return Easing(function: .curveTypeQuartic, type: .easeTypeOut) 47 | case 11:return Easing(function: .curveTypeQuartic, type: .easeTypeInOut) 48 | case 12:return Easing(function: .curveTypeQuintic, type: .easeTypeIn) 49 | case 13:return Easing(function: .curveTypeQuintic, type: .easeTypeOut) 50 | case 14:return Easing(function: .curveTypeQuintic, type: .easeTypeInOut) 51 | case 15:return Easing(function: .curveTypeSine, type: .easeTypeIn) 52 | case 16:return Easing(function: .curveTypeSine, type: .easeTypeOut) 53 | case 17:return Easing(function: .curveTypeSine, type: .easeTypeInOut) 54 | case 18:return Easing(function: .curveTypeExpo, type: .easeTypeIn) 55 | case 19:return Easing(function: .curveTypeExpo, type: .easeTypeOut) 56 | case 20:return Easing(function: .curveTypeExpo, type: .easeTypeInOut) 57 | case 21:return Easing(function: .curveTypeCircular, type: .easeTypeIn) 58 | case 22:return Easing(function: .curveTypeCircular, type: .easeTypeOut) 59 | case 23:return Easing(function: .curveTypeCircular, type: .easeTypeInOut) 60 | case 24:return Easing(function: .curveTypeElastic, type: .easeTypeIn) 61 | case 25:return Easing(function: .curveTypeElastic, type: .easeTypeOut) 62 | //ElasticHalf Out, function modified from plain elastic 63 | case 26:return Easing(function: .curveTypeElasticHalf, type: .easeTypeOut) 64 | //ElasticQuarter Out, function modified from plain elastic 65 | case 27:return Easing(function: .curveTypeElasticQuarter, type: .easeTypeOut) 66 | case 28:return Easing(function: .curveTypeElastic, type: .easeTypeInOut) 67 | case 29:return Easing(function: .curveTypeBack, type: .easeTypeIn) 68 | case 30:return Easing(function: .curveTypeBack, type: .easeTypeOut) 69 | case 31:return Easing(function: .curveTypeBack, type: .easeTypeInOut) 70 | case 32:return Easing(function: .curveTypeBounce, type: .easeTypeIn) 71 | case 33:return Easing(function: .curveTypeBounce, type: .easeTypeOut) 72 | case 34:return Easing(function: .curveTypeBounce, type: .easeTypeInOut) 73 | default:return Easing(function: .curveTypeLinear, type: .easeTypeIn) 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /iosu/StoryBoard/Renderer/SBCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SBCommand.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | protocol SBCAction { 13 | func toAction()->SKAction 14 | } 15 | 16 | class SBCommand { 17 | 18 | var type:StoryBoardCommand 19 | var easing:Easing 20 | var starttime:Int 21 | var endtime:Int 22 | var duration:Double 23 | //var sprite:SKSpriteNode? 24 | 25 | init(type:StoryBoardCommand,easing:Int,starttime:Int,endtime:Int) { 26 | self.type=type 27 | self.easing=SBCHelper.num2easing(easing) 28 | self.starttime=starttime 29 | self.endtime=endtime 30 | duration=(Double(endtime)-Double(starttime))/1000 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /iosu/StoryBoard/StoryBoardEnums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryBoardEnums.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/5/16. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum StoryBoardError:Error { 12 | case fileNotFound 13 | case illegalFormat 14 | } 15 | 16 | enum SBLayer { 17 | case background 18 | case fail 19 | case pass 20 | case foreground 21 | } 22 | 23 | enum SBOrigin { 24 | case topLeft 25 | case topCentre 26 | case topRight 27 | case centreLeft 28 | case centre 29 | case centreRight 30 | case bottomLeft 31 | case bottomCentre 32 | case bottomRight 33 | } 34 | 35 | enum LoopType { 36 | case loopForever 37 | case loopOnce 38 | } 39 | 40 | enum StoryBoardCommand { 41 | case fade 42 | case move 43 | case moveX 44 | case moveY 45 | case scale 46 | case vScale //x and y scale differently 47 | case rotate 48 | case color 49 | case parameter 50 | case loop 51 | case trigger 52 | case unknown 53 | } 54 | -------------------------------------------------------------------------------- /iosu/ViewControllers/AlertView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertView.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/4/5. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class Alerts { 13 | 14 | static func show(_ sender:UIViewController,title:String,message:String,style:UIAlertController.Style,actiontitle:String,actionstyle:UIAlertAction.Style,handler:((UIAlertAction) -> Void)?){ 15 | let alertController = UIAlertController(title: title, message: message, preferredStyle: style) 16 | let action=UIAlertAction(title: actiontitle, style: actionstyle, handler: handler) 17 | alertController.addAction(action) 18 | sender.present(alertController, animated: true, completion: nil) 19 | } 20 | 21 | static func show(_ sender:UIViewController,title:String,message:String,style:UIAlertController.Style,action1title:String,action1style:UIAlertAction.Style,handler1:((UIAlertAction) -> Void)?,action2title:String,action2style:UIAlertAction.Style,handler2:((UIAlertAction) -> Void)?){ 22 | let alertController = UIAlertController(title: title, message: message, preferredStyle: style) 23 | let action1=UIAlertAction(title: action1title, style: action1style, handler: handler1) 24 | let action2=UIAlertAction(title: action2title, style: action2style, handler: handler2) 25 | alertController.addAction(action1) 26 | alertController.addAction(action2) 27 | sender.present(alertController, animated: true, completion: nil) 28 | } 29 | 30 | static func create(_ title:String,message:String,style:UIAlertController.Style,actiontitle:String,actionstyle:UIAlertAction.Style,handler:((UIAlertAction) -> Void)?) -> UIAlertController!{ 31 | let alertController = UIAlertController(title: title, message: message, preferredStyle: style) 32 | let action=UIAlertAction(title: actiontitle, style: actionstyle, handler: handler) 33 | alertController.addAction(action) 34 | //debugPrint("alert created,\(alertController)") 35 | return alertController 36 | } 37 | 38 | static func create(_ title:String,message:String,style:UIAlertController.Style,action1title:String,action1style:UIAlertAction.Style,handler1:((UIAlertAction) -> Void)?,action2title:String,action2style:UIAlertAction.Style,handler2:((UIAlertAction) -> Void)?) -> UIAlertController!{ 39 | let alertController = UIAlertController(title: title, message: message, preferredStyle: style) 40 | let action1=UIAlertAction(title: action1title, style: action1style, handler: handler1) 41 | let action2=UIAlertAction(title: action2title, style: action2style, handler: handler2) 42 | alertController.addAction(action1) 43 | alertController.addAction(action2) 44 | //debugPrint("alert created,\(alertController)") 45 | return alertController 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /iosu/ViewControllers/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/3/28. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | import GameplayKit 12 | 13 | class GameViewController: UIViewController { 14 | 15 | let screenWidth=UIScreen.main.bounds.width*UIScreen.main.scale 16 | let screenHeight=UIScreen.main.bounds.height*UIScreen.main.scale 17 | static var showgame = true 18 | static var showvideo = true 19 | static var showsb = true 20 | var alert:UIAlertController! 21 | let runtestscene=false 22 | @IBOutlet weak var backBtn: UIButton! 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | var maxfps: Int 27 | if #available(iOS 10.3, *) { 28 | maxfps = UIScreen.main.maximumFramesPerSecond 29 | } else { 30 | maxfps = 60 31 | } 32 | debugPrint("Max FPS: \(maxfps)") 33 | view?.autoresizesSubviews=true 34 | view.backgroundColor = .black 35 | if runtestscene { 36 | let scene=TestScene(size: CGSize(width: screenWidth, height: screenHeight)) 37 | let skView=self.view as! SKView 38 | skView.preferredFramesPerSecond = maxfps 39 | skView.showsFPS=true 40 | skView.showsNodeCount=true 41 | skView.showsDrawCount=true 42 | skView.showsQuadCount=true 43 | skView.ignoresSiblingOrder=true 44 | skView.allowsTransparency=true 45 | scene.scaleMode = .aspectFit 46 | scene.backgroundColor = .cyan 47 | skView.presentScene(scene) 48 | return 49 | } 50 | if GameViewController.showsb { 51 | let sbScene=StoryBoardScene(size: CGSize(width: screenWidth, height: screenHeight),parent:self) 52 | let sbView=SKView(frame: UIScreen.main.bounds) 53 | sbView.layer.zPosition = 0 54 | self.view.addSubview(sbView) 55 | sbView.preferredFramesPerSecond = maxfps 56 | sbView.showsFPS=true 57 | sbView.showsNodeCount=true 58 | sbView.showsDrawCount=true 59 | sbView.showsQuadCount=true 60 | sbView.ignoresSiblingOrder=true 61 | sbView.layer.zPosition = 0 62 | sbScene.scaleMode = .aspectFit 63 | sbView.presentScene(sbScene) 64 | } 65 | if GameViewController.showvideo { 66 | //For video play 67 | BGVPlayer.myInitialize() 68 | BGVPlayer.view?.layer.zPosition = 1 69 | view.addSubview(BGVPlayer.view!) 70 | } 71 | if GameViewController.showgame { 72 | let gameScene=GamePlayScene(size: CGSize(width: screenWidth, height: screenHeight)) 73 | //let skView=self.view as! SKView 74 | let gameView=SKView(frame: UIScreen.main.bounds) 75 | gameView.preferredFramesPerSecond = maxfps 76 | gameView.layer.zPosition=2 77 | self.view.addSubview(gameView) 78 | //skView.allowsTransparency=true 79 | gameView.backgroundColor = .clear 80 | //(self.view as! SKView).allowsTransparency=true 81 | if GameViewController.showsb { 82 | gameView.showsFPS=false 83 | gameView.showsNodeCount=false 84 | } else { 85 | gameView.showsFPS=true 86 | gameView.showsNodeCount=true 87 | gameView.showsDrawCount=true 88 | gameView.showsQuadCount=true 89 | } 90 | gameView.ignoresSiblingOrder=true 91 | gameScene.scaleMode = .aspectFill 92 | gameScene.backgroundColor = .clear 93 | gameView.presentScene(gameScene) 94 | } 95 | BGMusicPlayer.instance.startPlaying() 96 | backBtn.removeFromSuperview() 97 | view.addSubview(backBtn) 98 | backBtn.layer.zPosition = 5 99 | } 100 | 101 | @IBAction func onBack(_ sender: Any) { 102 | BGMusicPlayer.instance.stop() 103 | BGVPlayer.stop() 104 | BGVPlayer.view?.removeFromSuperview() 105 | dismiss(animated: true, completion: nil) 106 | } 107 | 108 | override func viewDidAppear(_ animated: Bool) { 109 | debugPrint("scene appears,\(alert)") 110 | if alert != nil { 111 | debugPrint("show alert") 112 | self.present(alert!, animated: true, completion: nil) 113 | } 114 | } 115 | 116 | override var shouldAutorotate: Bool { 117 | return true 118 | } 119 | 120 | override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation{ 121 | return .landscapeLeft 122 | } 123 | 124 | override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 125 | if UIDevice.current.userInterfaceIdiom == .phone { 126 | return .landscape 127 | } else { 128 | return .landscape 129 | } 130 | } 131 | 132 | override func didReceiveMemoryWarning() { 133 | super.didReceiveMemoryWarning() 134 | // Release any cached data, images, etc that aren't in use. 135 | } 136 | 137 | override var prefersStatusBarHidden: Bool { 138 | return true 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /iosu/ViewControllers/SelectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionView.swift 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/4/3. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class SelectionViewController:UIViewController,UIPickerViewDelegate,UIPickerViewDataSource { 13 | 14 | @IBOutlet var picker: UIPickerView! 15 | 16 | @IBOutlet var gameSwitch: UISwitch! 17 | @IBOutlet var videoSwitch: UISwitch! 18 | @IBOutlet var sbSwitch: UISwitch! 19 | @IBOutlet var dimSlider: UISlider! 20 | @IBOutlet var dimLabel: UILabel! 21 | @IBOutlet var musicSlider: UISlider! 22 | @IBOutlet var musicLabel: UILabel! 23 | @IBOutlet var effectSlider: UISlider! 24 | @IBOutlet var effectLabel: UILabel! 25 | 26 | @IBOutlet var skinSwitch: UISwitch! 27 | 28 | let bs=BeatmapScanner() 29 | 30 | override func viewDidLoad() { 31 | picker.dataSource=self 32 | picker.delegate=self 33 | UIApplication.shared.isIdleTimerDisabled=true 34 | } 35 | 36 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 37 | return 1 38 | } 39 | 40 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 41 | return bs.beatmaps.count 42 | } 43 | 44 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 45 | return bs.beatmaps[row] 46 | } 47 | 48 | @IBAction func playPressed(_ sender: Any) { 49 | SkinBuffer.bmPath = bs.beatmapdirs[picker.selectedRow(inComponent: 0)] 50 | SkinBuffer.useSkin = skinSwitch.isOn 51 | GamePlayScene.testBMIndex = picker.selectedRow(inComponent: 0) 52 | StoryBoardScene.testBMIndex = picker.selectedRow(inComponent: 0) 53 | GamePlayScene.bgdim = Double(dimSlider.value)/100 54 | GameViewController.showgame = gameSwitch.isOn 55 | GameViewController.showvideo = videoSwitch.isOn 56 | GameViewController.showsb = sbSwitch.isOn 57 | BGMusicPlayer.instance.bgmvolume = musicSlider.value/100 58 | GamePlayScene.effvolume = effectSlider.value/100 59 | self.performSegue(withIdentifier: "play", sender: self.view) 60 | } 61 | 62 | @IBAction func dimChanged(_ sender: UISlider) { 63 | dimLabel.text = "\(Int(sender.value))%" 64 | } 65 | 66 | @IBAction func musicChanged(_ sender: UISlider) { 67 | musicLabel.text = "\(Int(sender.value))%" 68 | } 69 | 70 | @IBAction func effectChanged(_ sender: UISlider) { 71 | effectLabel.text = "\(Int(sender.value))%" 72 | } 73 | 74 | @IBAction func gameSwitched(_ sender: Any) { 75 | if !sbSwitch.isOn && !gameSwitch.isOn { 76 | Alerts.show(self, title: "Warning", message: "You should turn on either game or storyboard!", style: .alert, actiontitle: "OK", actionstyle: .default, handler: {(act:UIAlertAction) -> Void in 77 | self.gameSwitch.setOn(true, animated: true) 78 | self.skinSwitch.isEnabled = true 79 | }) 80 | } 81 | if gameSwitch.isOn { 82 | skinSwitch.isEnabled = true 83 | } else { 84 | skinSwitch.isEnabled = false 85 | } 86 | } 87 | @IBAction func sbSwitched(_ sender: Any) { 88 | if !sbSwitch.isOn && !gameSwitch.isOn { 89 | Alerts.show(self, title: "Warning", message: "You should turn on either game or storyboard!", style: .alert, actiontitle: "OK", actionstyle: .default, handler: {(act:UIAlertAction) -> Void in 90 | self.sbSwitch.setOn(true, animated: true) 91 | }) 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /iosu/audios/applause.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/applause.mp3 -------------------------------------------------------------------------------- /iosu/audios/combobreak.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/combobreak.mp3 -------------------------------------------------------------------------------- /iosu/audios/count.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/count.wav -------------------------------------------------------------------------------- /iosu/audios/count1s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/count1s.wav -------------------------------------------------------------------------------- /iosu/audios/count2s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/count2s.wav -------------------------------------------------------------------------------- /iosu/audios/count3s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/count3s.wav -------------------------------------------------------------------------------- /iosu/audios/drum-hitclap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-hitclap.wav -------------------------------------------------------------------------------- /iosu/audios/drum-hitfinish.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-hitfinish.wav -------------------------------------------------------------------------------- /iosu/audios/drum-hitnormal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-hitnormal.wav -------------------------------------------------------------------------------- /iosu/audios/drum-hitwhistle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-hitwhistle.wav -------------------------------------------------------------------------------- /iosu/audios/drum-sliderslide.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-sliderslide.wav -------------------------------------------------------------------------------- /iosu/audios/drum-slidertick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-slidertick.wav -------------------------------------------------------------------------------- /iosu/audios/drum-sliderwhistle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/drum-sliderwhistle.wav -------------------------------------------------------------------------------- /iosu/audios/failsound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/failsound.mp3 -------------------------------------------------------------------------------- /iosu/audios/gos.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/gos.wav -------------------------------------------------------------------------------- /iosu/audios/menuback.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/menuback.wav -------------------------------------------------------------------------------- /iosu/audios/menuclick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/menuclick.wav -------------------------------------------------------------------------------- /iosu/audios/menuhit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/menuhit.wav -------------------------------------------------------------------------------- /iosu/audios/normal-hitclap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-hitclap.wav -------------------------------------------------------------------------------- /iosu/audios/normal-hitfinish.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-hitfinish.wav -------------------------------------------------------------------------------- /iosu/audios/normal-hitnormal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-hitnormal.wav -------------------------------------------------------------------------------- /iosu/audios/normal-hitwhistle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-hitwhistle.wav -------------------------------------------------------------------------------- /iosu/audios/normal-sliderslide.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-sliderslide.wav -------------------------------------------------------------------------------- /iosu/audios/normal-slidertick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-slidertick.wav -------------------------------------------------------------------------------- /iosu/audios/normal-sliderwhistle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/normal-sliderwhistle.wav -------------------------------------------------------------------------------- /iosu/audios/readys.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/readys.wav -------------------------------------------------------------------------------- /iosu/audios/sectionfail.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/sectionfail.mp3 -------------------------------------------------------------------------------- /iosu/audios/sectionpass.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/sectionpass.mp3 -------------------------------------------------------------------------------- /iosu/audios/soft-hitclap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-hitclap.wav -------------------------------------------------------------------------------- /iosu/audios/soft-hitfinish.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-hitfinish.wav -------------------------------------------------------------------------------- /iosu/audios/soft-hitnormal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-hitnormal.wav -------------------------------------------------------------------------------- /iosu/audios/soft-hitwhistle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-hitwhistle.wav -------------------------------------------------------------------------------- /iosu/audios/soft-sliderslide.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-sliderslide.wav -------------------------------------------------------------------------------- /iosu/audios/soft-slidertick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-slidertick.wav -------------------------------------------------------------------------------- /iosu/audios/soft-sliderwhistle.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/soft-sliderwhistle.wav -------------------------------------------------------------------------------- /iosu/audios/spinnerbonus.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/spinnerbonus.wav -------------------------------------------------------------------------------- /iosu/audios/spinnerspin.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/iosu/audios/spinnerspin.wav -------------------------------------------------------------------------------- /iosu/iosu-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // iosu-Bridging-Header.h 3 | // iosu 4 | // 5 | // Created by xieyi on 2017/4/11. 6 | // Copyright © 2017年 xieyi. All rights reserved. 7 | // 8 | 9 | #ifndef iosu_Bridging_Header_h 10 | #define iosu_Bridging_Header_h 11 | 12 | //UIBezierPath-Length 13 | #import "UIBezierPath+Length.h" 14 | 15 | //MobileVLCKit 16 | #import "MobileVLCKit/MobileVLCKit.h" 17 | 18 | #endif /* iosu_Bridging_Header_h */ 19 | -------------------------------------------------------------------------------- /ipa.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | method 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /screenshots/import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/import.png -------------------------------------------------------------------------------- /screenshots/p_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/p_run.png -------------------------------------------------------------------------------- /screenshots/sb_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/sb_run.png -------------------------------------------------------------------------------- /screenshots/sbplayer1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/sbplayer1.png -------------------------------------------------------------------------------- /screenshots/sbplayer2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/sbplayer2.png -------------------------------------------------------------------------------- /screenshots/sbplayer3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/sbplayer3.png -------------------------------------------------------------------------------- /screenshots/sbplayer4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/sbplayer4.png -------------------------------------------------------------------------------- /screenshots/selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/selection.png -------------------------------------------------------------------------------- /screenshots/sk_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/sk_run.png -------------------------------------------------------------------------------- /screenshots/v_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imxieyi/iosu/b1524d670d27bf608e9fc89e776fc0d48adfb511/screenshots/v_run.png --------------------------------------------------------------------------------