├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── settings.json ├── CMakeLists.txt ├── CMakePresets.json ├── README.md ├── SDL_mixer.patch ├── Xcode └── FlappyBird │ ├── .DS_Store │ ├── FlappyBird.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── FlappyBird │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon 1.png │ │ ├── icon 2.png │ │ └── icon.png │ ├── ColorMap.textureset │ │ ├── Contents.json │ │ └── Universal.mipmapset │ │ │ ├── ColorMap.png │ │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ └── SDL_uikit_main.c ├── flappybird.c ├── icon.png ├── preview.gif ├── screenshot.png ├── sfx_die.wav ├── sfx_hit.wav ├── sfx_point.wav ├── sfx_swooshing.wav ├── sfx_wing.wav └── spritesheet.png /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | data.txt 4 | xcuserdata/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/SDL_mixer"] 2 | path = deps/SDL_mixer 3 | url = https://github.com/libsdl-org/SDL_mixer.git 4 | [submodule "deps/SDL_image"] 5 | path = deps/SDL_image 6 | url = https://github.com/libsdl-org/SDL_image.git 7 | [submodule "deps/SDL"] 8 | path = deps/SDL 9 | url = https://github.com/libsdl-org/SDL.git 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${command:cmake.launchTargetPath}", 9 | "args": [], 10 | "stopAtEntry": false, 11 | "cwd": "${workspaceFolder}", 12 | "environment": [], 13 | "MIMode": "lldb" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "sdl.h": "c", 4 | "begin_code.h": "c", 5 | "ios": "c", 6 | "__config": "c", 7 | "algorithm": "c" 8 | } 9 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(flappybird) 4 | 5 | add_subdirectory(deps/SDL) 6 | 7 | add_subdirectory(deps/SDL_image) 8 | 9 | set(SDL2MIXER_OPUS OFF) 10 | add_subdirectory(deps/SDL_mixer) 11 | 12 | # Create your game executable target as usual 13 | add_executable(flappybird flappybird.c) 14 | 15 | set_property(TARGET flappybird PROPERTY COMPILE_WARNING_AS_ERROR ON) 16 | 17 | target_include_directories(flappybird PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_image_INCLUDE_DIRS} ${SDL2_mixer_INCLUDE_DIRS}) 18 | 19 | # SDL2::SDL2main may or may not be available. It is e.g. required by Windows GUI applications 20 | if(TARGET SDL2::SDL2main) 21 | # It has an implicit dependency on SDL2 functions, so it MUST be added before SDL2::SDL2 (or SDL2::SDL2-static) 22 | target_link_libraries(flappybird PRIVATE SDL2::SDL2main) 23 | endif() 24 | 25 | # Link to the actual SDL2 library. SDL2::SDL2 is the shared SDL library, SDL2::SDL2-static is the static SDL libarary. 26 | target_link_libraries(flappybird PRIVATE SDL2::SDL2 SDL2_image::SDL2_image SDL2_mixer::SDL2_mixer) 27 | 28 | find_library(MATH_LIBRARY m) 29 | if (MATH_LIBRARY) 30 | target_link_libraries(flappybird PRIVATE ${MATH_LIBRARY}) 31 | endif() 32 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 8, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 29, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "Debug", 11 | "hidden": false, 12 | "description": "Debug build", 13 | "binaryDir": "${sourceDir}/build/Debug", 14 | "cacheVariables": { 15 | "CMAKE_BUILD_TYPE": "Debug" 16 | } 17 | }, 18 | { 19 | "name": "Release", 20 | "hidden": false, 21 | "description": "Release build", 22 | "binaryDir": "${sourceDir}/build/Release", 23 | "cacheVariables": { 24 | "CMAKE_BUILD_TYPE": "Release" 25 | } 26 | }, 27 | { 28 | "name": "Profile", 29 | "hidden": false, 30 | "description": "Profile build", 31 | "binaryDir": "${sourceDir}/build/Profile", 32 | "cacheVariables": { 33 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 34 | } 35 | } 36 | ], 37 | "buildPresets": [ 38 | { 39 | "name": "Debug", 40 | "configurePreset": "Debug", 41 | "jobs": 8 42 | }, 43 | { 44 | "name": "Release", 45 | "configurePreset": "Release", 46 | "jobs": 8 47 | }, 48 | { 49 | "name": "Profile", 50 | "configurePreset": "Profile", 51 | "jobs": 8 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flappy Bird 2 | 3 | A clone of [.GEARS' Flappy Bird](https://dotgears.com/games/flappy-birds-family) in just over 1000 lines of C. 4 | 5 | ![Preview](preview.gif "Preview") 6 | 7 | ## Checkout 8 | 9 | ### A fresh one 10 | 11 | ``` 12 | git clone --recurse-submodules https://github.com/alxyng/flappybird.git 13 | ``` 14 | 15 | ### Existing one 16 | 17 | ``` 18 | git clone https://github.com/alxyng/flappybird.git 19 | git submodule update --init --recursive 20 | ``` 21 | 22 | ## Build and run 23 | 24 | ### macOS 25 | 26 | ``` 27 | brew install libxmp fluid-synth wavpack 28 | cmake --preset Release 29 | cmake --build --preset Release 30 | ./build/Release/flappybird 31 | ``` 32 | 33 | ### Debian/Linux 34 | 35 | ``` 36 | sudo apt-get install libxmp-dev libwavpack-dev libfluidsynth-dev fluidsynth 37 | cmake --preset Release 38 | cmake --build --preset Release 39 | ./build/Release/flappybird 40 | ``` 41 | 42 | ### iOS 43 | 44 | The following has been tested on XCode 16.0. 45 | 46 | From the root of the repo, apply the following patch to SDL_mixer: 47 | 48 | ``` 49 | (cd deps/SDL_mixer && git apply ../../SDL_mixer.patch) 50 | ``` 51 | 52 | Then open XCode, select a device and hit Run. You may have to set up signing if you haven't done so already. -------------------------------------------------------------------------------- /SDL_mixer.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Xcode/SDL_mixer.xcodeproj/project.pbxproj b/Xcode/SDL_mixer.xcodeproj/project.pbxproj 2 | index b46c970f..a078481a 100644 3 | --- a/Xcode/SDL_mixer.xcodeproj/project.pbxproj 4 | +++ b/Xcode/SDL_mixer.xcodeproj/project.pbxproj 5 | @@ -851,7 +851,7 @@ 6 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 7 | GCC_NO_COMMON_BLOCKS = YES; 8 | GCC_PREPROCESSOR_DEFINITIONS = ( 9 | - MUSIC_FLAC_DRFLAC, 10 | + MUSIC_FLAC_DRFLACX, 11 | MUSIC_MP3_MINIMP3, 12 | MUSIC_OGG, 13 | OGG_USE_STB, 14 | @@ -901,7 +901,7 @@ 15 | GCC_NO_COMMON_BLOCKS = YES; 16 | GCC_OPTIMIZATION_LEVEL = 0; 17 | GCC_PREPROCESSOR_DEFINITIONS = ( 18 | - MUSIC_FLAC_DRFLAC, 19 | + MUSIC_FLAC_DRFLACX, 20 | MUSIC_MP3_MINIMP3, 21 | MUSIC_OGG, 22 | OGG_USE_STB, 23 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/Xcode/FlappyBird/.DS_Store -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 77; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FA2B5F2F2CC81D0C0060DA75 /* spritesheet.png in Resources */ = {isa = PBXBuildFile; fileRef = FA2B5F2E2CC81D0C0060DA75 /* spritesheet.png */; }; 11 | FA2B5F3F2CC861330060DA75 /* sfx_die.wav in Resources */ = {isa = PBXBuildFile; fileRef = FA2B5F3A2CC861330060DA75 /* sfx_die.wav */; }; 12 | FA2B5F402CC861330060DA75 /* sfx_hit.wav in Resources */ = {isa = PBXBuildFile; fileRef = FA2B5F3B2CC861330060DA75 /* sfx_hit.wav */; }; 13 | FA2B5F412CC861330060DA75 /* sfx_wing.wav in Resources */ = {isa = PBXBuildFile; fileRef = FA2B5F3E2CC861330060DA75 /* sfx_wing.wav */; }; 14 | FA2B5F422CC861330060DA75 /* sfx_swooshing.wav in Resources */ = {isa = PBXBuildFile; fileRef = FA2B5F3D2CC861330060DA75 /* sfx_swooshing.wav */; }; 15 | FA2B5F432CC861330060DA75 /* sfx_point.wav in Resources */ = {isa = PBXBuildFile; fileRef = FA2B5F3C2CC861330060DA75 /* sfx_point.wav */; }; 16 | FA2B5F5F2CC865FB0060DA75 /* SDL2_mixer.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FA2B5F522CC865C30060DA75 /* SDL2_mixer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | FA2B5F612CC868E40060DA75 /* SDL2_mixer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA2B5F522CC865C30060DA75 /* SDL2_mixer.framework */; }; 18 | FA2B5FA12CC86E5E0060DA75 /* SDL2_image.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA2B5F942CC86E300060DA75 /* SDL2_image.framework */; }; 19 | FA2B5FA22CC86E5E0060DA75 /* SDL2_image.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FA2B5F942CC86E300060DA75 /* SDL2_image.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | FA2B5FA32CC86E600060DA75 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA2B5F752CC86E1E0060DA75 /* SDL2.framework */; }; 21 | FA2B5FA42CC86E600060DA75 /* SDL2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FA2B5F752CC86E1E0060DA75 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | FAC6B8B32CCB90D700E85E01 /* flappybird.c in Sources */ = {isa = PBXBuildFile; fileRef = FAC6B8B22CCB90D700E85E01 /* flappybird.c */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | FA2B5F512CC865C30060DA75 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = FA2B5F442CC865C30060DA75 /* SDL_mixer.xcodeproj */; 29 | proxyType = 2; 30 | remoteGlobalIDString = BE1FA90607AF96B2004B6283; 31 | remoteInfo = Framework; 32 | }; 33 | FA2B5F742CC86E1E0060DA75 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = FA2B5F622CC86E1D0060DA75 /* SDL.xcodeproj */; 36 | proxyType = 2; 37 | remoteGlobalIDString = A7D88B5423E2437C00DCD162; 38 | remoteInfo = "Framework-iOS"; 39 | }; 40 | FA2B5F932CC86E300060DA75 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = FA2B5F882CC86E300060DA75 /* SDL_image.xcodeproj */; 43 | proxyType = 2; 44 | remoteGlobalIDString = BE1FA72E07AF4C45004B6283; 45 | remoteInfo = Framework; 46 | }; 47 | /* End PBXContainerItemProxy section */ 48 | 49 | /* Begin PBXCopyFilesBuildPhase section */ 50 | FA9C8B2C2CC7E1C200B46CC7 /* Embed Frameworks */ = { 51 | isa = PBXCopyFilesBuildPhase; 52 | buildActionMask = 2147483647; 53 | dstPath = ""; 54 | dstSubfolderSpec = 10; 55 | files = ( 56 | FA2B5FA42CC86E600060DA75 /* SDL2.framework in Embed Frameworks */, 57 | FA2B5F5F2CC865FB0060DA75 /* SDL2_mixer.framework in Embed Frameworks */, 58 | FA2B5FA22CC86E5E0060DA75 /* SDL2_image.framework in Embed Frameworks */, 59 | ); 60 | name = "Embed Frameworks"; 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXCopyFilesBuildPhase section */ 64 | 65 | /* Begin PBXFileReference section */ 66 | FA2B5F2E2CC81D0C0060DA75 /* spritesheet.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = spritesheet.png; path = /Users/alex/development/flappybird/spritesheet.png; sourceTree = ""; }; 67 | FA2B5F3A2CC861330060DA75 /* sfx_die.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sfx_die.wav; path = /Users/alex/development/flappybird/sfx_die.wav; sourceTree = ""; }; 68 | FA2B5F3B2CC861330060DA75 /* sfx_hit.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sfx_hit.wav; path = /Users/alex/development/flappybird/sfx_hit.wav; sourceTree = ""; }; 69 | FA2B5F3C2CC861330060DA75 /* sfx_point.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sfx_point.wav; path = /Users/alex/development/flappybird/sfx_point.wav; sourceTree = ""; }; 70 | FA2B5F3D2CC861330060DA75 /* sfx_swooshing.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sfx_swooshing.wav; path = /Users/alex/development/flappybird/sfx_swooshing.wav; sourceTree = ""; }; 71 | FA2B5F3E2CC861330060DA75 /* sfx_wing.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = sfx_wing.wav; path = /Users/alex/development/flappybird/sfx_wing.wav; sourceTree = ""; }; 72 | FA2B5F442CC865C30060DA75 /* SDL_mixer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL_mixer.xcodeproj; path = /Users/alex/development/flappybird/deps/SDL_mixer/Xcode/SDL_mixer.xcodeproj; sourceTree = ""; }; 73 | FA2B5F622CC86E1D0060DA75 /* SDL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL.xcodeproj; path = /Users/alex/development/flappybird/deps/SDL/Xcode/SDL/SDL.xcodeproj; sourceTree = ""; }; 74 | FA2B5F882CC86E300060DA75 /* SDL_image.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL_image.xcodeproj; path = /Users/alex/development/flappybird/deps/SDL_image/Xcode/SDL_image.xcodeproj; sourceTree = ""; }; 75 | FA9C8AE42CC7E07800B46CC7 /* Flappy Bird.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Flappy Bird.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | FAC6B8B22CCB90D700E85E01 /* flappybird.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = flappybird.c; path = /Users/alex/development/flappybird/flappybird.c; sourceTree = ""; }; 77 | /* End PBXFileReference section */ 78 | 79 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 80 | FA9C8AE62CC7E07800B46CC7 /* FlappyBird */ = { 81 | isa = PBXFileSystemSynchronizedRootGroup; 82 | path = FlappyBird; 83 | sourceTree = ""; 84 | }; 85 | /* End PBXFileSystemSynchronizedRootGroup section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | FA9C8AE12CC7E07800B46CC7 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | FA2B5FA32CC86E600060DA75 /* SDL2.framework in Frameworks */, 93 | FA2B5FA12CC86E5E0060DA75 /* SDL2_image.framework in Frameworks */, 94 | FA2B5F612CC868E40060DA75 /* SDL2_mixer.framework in Frameworks */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | /* End PBXFrameworksBuildPhase section */ 99 | 100 | /* Begin PBXGroup section */ 101 | FA2B5F4C2CC865C30060DA75 /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | FA2B5F522CC865C30060DA75 /* SDL2_mixer.framework */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | FA2B5F652CC86E1E0060DA75 /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | FA2B5F752CC86E1E0060DA75 /* SDL2.framework */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | FA2B5F8E2CC86E300060DA75 /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | FA2B5F942CC86E300060DA75 /* SDL2_image.framework */, 121 | ); 122 | name = Products; 123 | sourceTree = ""; 124 | }; 125 | FA9C8ADB2CC7E07800B46CC7 = { 126 | isa = PBXGroup; 127 | children = ( 128 | FAC6B8B22CCB90D700E85E01 /* flappybird.c */, 129 | FA2B5F622CC86E1D0060DA75 /* SDL.xcodeproj */, 130 | FA2B5F882CC86E300060DA75 /* SDL_image.xcodeproj */, 131 | FA2B5F442CC865C30060DA75 /* SDL_mixer.xcodeproj */, 132 | FA2B5F3A2CC861330060DA75 /* sfx_die.wav */, 133 | FA2B5F3B2CC861330060DA75 /* sfx_hit.wav */, 134 | FA2B5F3C2CC861330060DA75 /* sfx_point.wav */, 135 | FA2B5F3D2CC861330060DA75 /* sfx_swooshing.wav */, 136 | FA2B5F3E2CC861330060DA75 /* sfx_wing.wav */, 137 | FA2B5F2E2CC81D0C0060DA75 /* spritesheet.png */, 138 | FA9C8AE62CC7E07800B46CC7 /* FlappyBird */, 139 | FA9C8B282CC7E1B500B46CC7 /* Frameworks */, 140 | FA9C8AE52CC7E07800B46CC7 /* Products */, 141 | ); 142 | sourceTree = ""; 143 | }; 144 | FA9C8AE52CC7E07800B46CC7 /* Products */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | FA9C8AE42CC7E07800B46CC7 /* Flappy Bird.app */, 148 | ); 149 | name = Products; 150 | sourceTree = ""; 151 | }; 152 | FA9C8B282CC7E1B500B46CC7 /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | ); 156 | name = Frameworks; 157 | sourceTree = ""; 158 | }; 159 | /* End PBXGroup section */ 160 | 161 | /* Begin PBXNativeTarget section */ 162 | FA9C8AE32CC7E07800B46CC7 /* FlappyBird */ = { 163 | isa = PBXNativeTarget; 164 | buildConfigurationList = FA9C8AFF2CC7E07900B46CC7 /* Build configuration list for PBXNativeTarget "FlappyBird" */; 165 | buildPhases = ( 166 | FA9C8AE02CC7E07800B46CC7 /* Sources */, 167 | FA9C8AE12CC7E07800B46CC7 /* Frameworks */, 168 | FA9C8AE22CC7E07800B46CC7 /* Resources */, 169 | FA9C8B2C2CC7E1C200B46CC7 /* Embed Frameworks */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | ); 175 | fileSystemSynchronizedGroups = ( 176 | FA9C8AE62CC7E07800B46CC7 /* FlappyBird */, 177 | ); 178 | name = FlappyBird; 179 | packageProductDependencies = ( 180 | ); 181 | productName = FlappyBird; 182 | productReference = FA9C8AE42CC7E07800B46CC7 /* Flappy Bird.app */; 183 | productType = "com.apple.product-type.application"; 184 | }; 185 | /* End PBXNativeTarget section */ 186 | 187 | /* Begin PBXProject section */ 188 | FA9C8ADC2CC7E07800B46CC7 /* Project object */ = { 189 | isa = PBXProject; 190 | attributes = { 191 | BuildIndependentTargetsInParallel = 1; 192 | LastUpgradeCheck = 1600; 193 | TargetAttributes = { 194 | FA9C8AE32CC7E07800B46CC7 = { 195 | CreatedOnToolsVersion = 16.0; 196 | }; 197 | }; 198 | }; 199 | buildConfigurationList = FA9C8ADF2CC7E07800B46CC7 /* Build configuration list for PBXProject "FlappyBird" */; 200 | developmentRegion = en; 201 | hasScannedForEncodings = 0; 202 | knownRegions = ( 203 | en, 204 | Base, 205 | ); 206 | mainGroup = FA9C8ADB2CC7E07800B46CC7; 207 | minimizedProjectReferenceProxies = 1; 208 | preferredProjectObjectVersion = 77; 209 | productRefGroup = FA9C8AE52CC7E07800B46CC7 /* Products */; 210 | projectDirPath = ""; 211 | projectReferences = ( 212 | { 213 | ProductGroup = FA2B5F652CC86E1E0060DA75 /* Products */; 214 | ProjectRef = FA2B5F622CC86E1D0060DA75 /* SDL.xcodeproj */; 215 | }, 216 | { 217 | ProductGroup = FA2B5F8E2CC86E300060DA75 /* Products */; 218 | ProjectRef = FA2B5F882CC86E300060DA75 /* SDL_image.xcodeproj */; 219 | }, 220 | { 221 | ProductGroup = FA2B5F4C2CC865C30060DA75 /* Products */; 222 | ProjectRef = FA2B5F442CC865C30060DA75 /* SDL_mixer.xcodeproj */; 223 | }, 224 | ); 225 | projectRoot = ""; 226 | targets = ( 227 | FA9C8AE32CC7E07800B46CC7 /* FlappyBird */, 228 | ); 229 | }; 230 | /* End PBXProject section */ 231 | 232 | /* Begin PBXReferenceProxy section */ 233 | FA2B5F522CC865C30060DA75 /* SDL2_mixer.framework */ = { 234 | isa = PBXReferenceProxy; 235 | fileType = wrapper.framework; 236 | path = SDL2_mixer.framework; 237 | remoteRef = FA2B5F512CC865C30060DA75 /* PBXContainerItemProxy */; 238 | sourceTree = BUILT_PRODUCTS_DIR; 239 | }; 240 | FA2B5F752CC86E1E0060DA75 /* SDL2.framework */ = { 241 | isa = PBXReferenceProxy; 242 | fileType = wrapper.framework; 243 | path = SDL2.framework; 244 | remoteRef = FA2B5F742CC86E1E0060DA75 /* PBXContainerItemProxy */; 245 | sourceTree = BUILT_PRODUCTS_DIR; 246 | }; 247 | FA2B5F942CC86E300060DA75 /* SDL2_image.framework */ = { 248 | isa = PBXReferenceProxy; 249 | fileType = wrapper.framework; 250 | path = SDL2_image.framework; 251 | remoteRef = FA2B5F932CC86E300060DA75 /* PBXContainerItemProxy */; 252 | sourceTree = BUILT_PRODUCTS_DIR; 253 | }; 254 | /* End PBXReferenceProxy section */ 255 | 256 | /* Begin PBXResourcesBuildPhase section */ 257 | FA9C8AE22CC7E07800B46CC7 /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | FA2B5F2F2CC81D0C0060DA75 /* spritesheet.png in Resources */, 262 | FA2B5F3F2CC861330060DA75 /* sfx_die.wav in Resources */, 263 | FA2B5F402CC861330060DA75 /* sfx_hit.wav in Resources */, 264 | FA2B5F412CC861330060DA75 /* sfx_wing.wav in Resources */, 265 | FA2B5F422CC861330060DA75 /* sfx_swooshing.wav in Resources */, 266 | FA2B5F432CC861330060DA75 /* sfx_point.wav in Resources */, 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | /* End PBXResourcesBuildPhase section */ 271 | 272 | /* Begin PBXSourcesBuildPhase section */ 273 | FA9C8AE02CC7E07800B46CC7 /* Sources */ = { 274 | isa = PBXSourcesBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | FAC6B8B32CCB90D700E85E01 /* flappybird.c in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin XCBuildConfiguration section */ 284 | FA9C8AFD2CC7E07900B46CC7 /* Debug */ = { 285 | isa = XCBuildConfiguration; 286 | buildSettings = { 287 | ALWAYS_SEARCH_USER_PATHS = NO; 288 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 289 | CLANG_ANALYZER_NONNULL = YES; 290 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_ENABLE_OBJC_WEAK = YES; 295 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 296 | CLANG_WARN_BOOL_CONVERSION = YES; 297 | CLANG_WARN_COMMA = YES; 298 | CLANG_WARN_CONSTANT_CONVERSION = YES; 299 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 300 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 301 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 302 | CLANG_WARN_EMPTY_BODY = YES; 303 | CLANG_WARN_ENUM_CONVERSION = YES; 304 | CLANG_WARN_INFINITE_RECURSION = YES; 305 | CLANG_WARN_INT_CONVERSION = YES; 306 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 307 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 308 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 310 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 315 | CLANG_WARN_UNREACHABLE_CODE = YES; 316 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 317 | COPY_PHASE_STRIP = NO; 318 | DEBUG_INFORMATION_FORMAT = dwarf; 319 | ENABLE_STRICT_OBJC_MSGSEND = YES; 320 | ENABLE_TESTABILITY = YES; 321 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 322 | GCC_C_LANGUAGE_STANDARD = gnu17; 323 | GCC_DYNAMIC_NO_PIC = NO; 324 | GCC_NO_COMMON_BLOCKS = YES; 325 | GCC_OPTIMIZATION_LEVEL = 0; 326 | GCC_PREPROCESSOR_DEFINITIONS = ( 327 | "DEBUG=1", 328 | "$(inherited)", 329 | ); 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 18.0; 337 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 338 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 339 | MTL_FAST_MATH = YES; 340 | ONLY_ACTIVE_ARCH = YES; 341 | SDKROOT = iphoneos; 342 | }; 343 | name = Debug; 344 | }; 345 | FA9C8AFE2CC7E07900B46CC7 /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ALWAYS_SEARCH_USER_PATHS = NO; 349 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 350 | CLANG_ANALYZER_NONNULL = YES; 351 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 353 | CLANG_ENABLE_MODULES = YES; 354 | CLANG_ENABLE_OBJC_ARC = YES; 355 | CLANG_ENABLE_OBJC_WEAK = YES; 356 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 357 | CLANG_WARN_BOOL_CONVERSION = YES; 358 | CLANG_WARN_COMMA = YES; 359 | CLANG_WARN_CONSTANT_CONVERSION = YES; 360 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 361 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 362 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 363 | CLANG_WARN_EMPTY_BODY = YES; 364 | CLANG_WARN_ENUM_CONVERSION = YES; 365 | CLANG_WARN_INFINITE_RECURSION = YES; 366 | CLANG_WARN_INT_CONVERSION = YES; 367 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 368 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 369 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 370 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 371 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 372 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 373 | CLANG_WARN_STRICT_PROTOTYPES = YES; 374 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 375 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 376 | CLANG_WARN_UNREACHABLE_CODE = YES; 377 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 378 | COPY_PHASE_STRIP = NO; 379 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 380 | ENABLE_NS_ASSERTIONS = NO; 381 | ENABLE_STRICT_OBJC_MSGSEND = YES; 382 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 383 | GCC_C_LANGUAGE_STANDARD = gnu17; 384 | GCC_NO_COMMON_BLOCKS = YES; 385 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 386 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 387 | GCC_WARN_UNDECLARED_SELECTOR = YES; 388 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 389 | GCC_WARN_UNUSED_FUNCTION = YES; 390 | GCC_WARN_UNUSED_VARIABLE = YES; 391 | IPHONEOS_DEPLOYMENT_TARGET = 18.0; 392 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 393 | MTL_ENABLE_DEBUG_INFO = NO; 394 | MTL_FAST_MATH = YES; 395 | SDKROOT = iphoneos; 396 | VALIDATE_PRODUCT = YES; 397 | }; 398 | name = Release; 399 | }; 400 | FA9C8B002CC7E07900B46CC7 /* Debug */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 404 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 405 | CODE_SIGN_STYLE = Automatic; 406 | CURRENT_PROJECT_VERSION = 1; 407 | DEVELOPMENT_TEAM = C2ARW9R4XQ; 408 | GENERATE_INFOPLIST_FILE = YES; 409 | HEADER_SEARCH_PATHS = ( 410 | "\"$(SRCROOT)/../../deps/SDL/include\"", 411 | "\"$(SRCROOT)/../../deps/SDL_image/include\"", 412 | "\"$(SRCROOT)/../../deps/SDL_mixer/include\"", 413 | ); 414 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 415 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 416 | INFOPLIST_KEY_UIRequiredDeviceCapabilities = metal; 417 | INFOPLIST_KEY_UIStatusBarHidden = YES; 418 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 419 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 420 | IPHONEOS_DEPLOYMENT_TARGET = 15.6; 421 | LD_RUNPATH_SEARCH_PATHS = ( 422 | "$(inherited)", 423 | "@executable_path/Frameworks", 424 | ); 425 | MARKETING_VERSION = 1.0; 426 | PRODUCT_BUNDLE_IDENTIFIER = com.alxyng.FlappyBird; 427 | PRODUCT_NAME = "Flappy Bird"; 428 | SWIFT_EMIT_LOC_STRINGS = YES; 429 | TARGETED_DEVICE_FAMILY = "1,2"; 430 | }; 431 | name = Debug; 432 | }; 433 | FA9C8B012CC7E07900B46CC7 /* Release */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 437 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 438 | CODE_SIGN_STYLE = Automatic; 439 | CURRENT_PROJECT_VERSION = 1; 440 | DEVELOPMENT_TEAM = C2ARW9R4XQ; 441 | GENERATE_INFOPLIST_FILE = YES; 442 | HEADER_SEARCH_PATHS = ( 443 | "\"$(SRCROOT)/../../deps/SDL/include\"", 444 | "\"$(SRCROOT)/../../deps/SDL_image/include\"", 445 | "\"$(SRCROOT)/../../deps/SDL_mixer/include\"", 446 | ); 447 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 448 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 449 | INFOPLIST_KEY_UIRequiredDeviceCapabilities = metal; 450 | INFOPLIST_KEY_UIStatusBarHidden = YES; 451 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 452 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 453 | IPHONEOS_DEPLOYMENT_TARGET = 15.6; 454 | LD_RUNPATH_SEARCH_PATHS = ( 455 | "$(inherited)", 456 | "@executable_path/Frameworks", 457 | ); 458 | MARKETING_VERSION = 1.0; 459 | PRODUCT_BUNDLE_IDENTIFIER = com.alxyng.FlappyBird; 460 | PRODUCT_NAME = "Flappy Bird"; 461 | SWIFT_EMIT_LOC_STRINGS = YES; 462 | TARGETED_DEVICE_FAMILY = "1,2"; 463 | }; 464 | name = Release; 465 | }; 466 | /* End XCBuildConfiguration section */ 467 | 468 | /* Begin XCConfigurationList section */ 469 | FA9C8ADF2CC7E07800B46CC7 /* Build configuration list for PBXProject "FlappyBird" */ = { 470 | isa = XCConfigurationList; 471 | buildConfigurations = ( 472 | FA9C8AFD2CC7E07900B46CC7 /* Debug */, 473 | FA9C8AFE2CC7E07900B46CC7 /* Release */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | FA9C8AFF2CC7E07900B46CC7 /* Build configuration list for PBXNativeTarget "FlappyBird" */ = { 479 | isa = XCConfigurationList; 480 | buildConfigurations = ( 481 | FA9C8B002CC7E07900B46CC7 /* Debug */, 482 | FA9C8B012CC7E07900B46CC7 /* Release */, 483 | ); 484 | defaultConfigurationIsVisible = 0; 485 | defaultConfigurationName = Release; 486 | }; 487 | /* End XCConfigurationList section */ 488 | }; 489 | rootObject = FA9C8ADC2CC7E07800B46CC7 /* Project object */; 490 | } 491 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon 1.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | }, 9 | { 10 | "appearances" : [ 11 | { 12 | "appearance" : "luminosity", 13 | "value" : "dark" 14 | } 15 | ], 16 | "filename" : "icon 2.png", 17 | "idiom" : "universal", 18 | "platform" : "ios", 19 | "size" : "1024x1024" 20 | }, 21 | { 22 | "appearances" : [ 23 | { 24 | "appearance" : "luminosity", 25 | "value" : "tinted" 26 | } 27 | ], 28 | "filename" : "icon.png", 29 | "idiom" : "universal", 30 | "platform" : "ios", 31 | "size" : "1024x1024" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/icon 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/icon 1.png -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/icon 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/icon 2.png -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/Xcode/FlappyBird/FlappyBird/Assets.xcassets/AppIcon.appiconset/icon.png -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/ColorMap.textureset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "properties" : { 7 | "origin" : "bottom-left", 8 | "interpretation" : "non-premultiplied-colors" 9 | }, 10 | "textures" : [ 11 | { 12 | "idiom" : "universal", 13 | "filename" : "Universal.mipmapset" 14 | } 15 | ] 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/ColorMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/Xcode/FlappyBird/FlappyBird/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/ColorMap.png -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/ColorMap.textureset/Universal.mipmapset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "levels" : [ 7 | { 8 | "filename" : "ColorMap.png", 9 | "mipmap-level" : "base" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/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 | -------------------------------------------------------------------------------- /Xcode/FlappyBird/FlappyBird/SDL_uikit_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | SDL_uikit_main.c, placed in the public domain by Sam Lantinga 3/18/2019 3 | */ 4 | 5 | /* Include the SDL main definition header */ 6 | #include "SDL_main.h" 7 | 8 | #if defined(__IPHONEOS__) || defined(__TVOS__) 9 | 10 | #ifndef SDL_MAIN_HANDLED 11 | #ifdef main 12 | #undef main 13 | #endif 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | return SDL_UIKitRunApp(argc, argv, SDL_main); 18 | } 19 | #endif /* !SDL_MAIN_HANDLED */ 20 | 21 | #endif /* __IPHONEOS__ || __TVOS__ */ 22 | 23 | /* vi: set ts=4 sw=4 expandtab: */ 24 | -------------------------------------------------------------------------------- /flappybird.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(__IPHONEOS__) 9 | const Uint32 Window_Flags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI; 10 | #else 11 | const Uint32 Window_Flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; 12 | #endif 13 | 14 | #define COLLISION_DETECTION 1 15 | #define DRAW_PLAYER_COLLIDER 0 16 | 17 | // Sprite Mapping 18 | 19 | const SDL_Rect Sprite_Background = { .x = 3, .y = 0, .w = 144, .h = 256 }; 20 | const SDL_Rect Sprite_Ground = { .x = 215, .y = 10, .w = 12, .h = 56 }; 21 | const SDL_Rect Sprite_Logo = { .x = 152, .y = 200, .w = 89, .h = 24 }; 22 | const SDL_Rect Sprite_Button_Start = { .x = 212, .y = 230, .w = 40, .h = 14 }; 23 | const SDL_Rect Sprite_Button_OK = { .x = 212, .y = 154, .w = 40, .h = 14 }; 24 | const SDL_Rect Sprite_Button_Pause = { .x = 261, .y = 174, .w = 13, .h = 14 }; 25 | const SDL_Rect Sprite_Button_Play = { .x = 412, .y = 94, .w = 13, .h = 14 }; 26 | const SDL_Rect Sprite_Get_Ready = { .x = 254, .y = 71, .w = 92, .h = 25 }; 27 | const SDL_Rect Sprite_Tap = { .x = 370, .y = 43, .w = 57, .h = 49 }; 28 | const SDL_Rect Sprite_Game_Over = { .x = 152, .y = 173, .w = 96, .h = 21 }; 29 | const SDL_Rect Sprite_Board = { .x = 260, .y = 195, .w = 113, .h = 57 }; 30 | const SDL_Rect Sprite_New = { .x = 214, .y = 126, .w = 16, .h = 7 }; 31 | const SDL_Rect Sprite_Medal_Bronze = { .x = 214, .y = 102, .w = 22, .h = 22 }; 32 | const SDL_Rect Sprite_Medal_Silver = { .x = 214, .y = 78, .w = 22, .h = 22 }; 33 | const SDL_Rect Sprite_Medal_Gold = { .x = 384, .y = 154, .w = 22, .h = 22 }; 34 | const SDL_Rect Sprite_Medal_Platinum = { .x = 384, .y = 130, .w = 22, .h = 22 }; 35 | const SDL_Rect Sprite_Pipe = { .x = 152, .y = 3, .w = 26, .h = 147 }; 36 | const SDL_Rect Sprite_Pipe_Top = { .x = 152, .y = 150, .w = 26, .h = 13 }; 37 | const SDL_Rect Sprite_Pipe_Bottom = { .x = 180, .y = 3, .w = 26, .h = 13 }; 38 | const SDL_Rect Sprite_Players[3] = { 39 | { .x = 381, .y = 187, .w = 17, .h = 12 }, 40 | { .x = 381, .y = 213, .w = 17, .h = 12 }, 41 | { .x = 381, .y = 239, .w = 17, .h = 12 }, 42 | }; 43 | 44 | const SDL_Rect Sprite_Numbers[10] = { 45 | { .x = 254, .y = 98, .w = 12, .h = 18 }, 46 | { .x = 238, .y = 80, .w = 8, .h = 18 }, 47 | { .x = 325, .y = 148, .w = 12, .h = 18 }, 48 | { .x = 339, .y = 148, .w = 12, .h = 18 }, 49 | { .x = 353, .y = 148, .w = 12, .h = 18 }, 50 | { .x = 367, .y = 148, .w = 12, .h = 18 }, 51 | { .x = 325, .y = 172, .w = 12, .h = 18 }, 52 | { .x = 339, .y = 172, .w = 12, .h = 18 }, 53 | { .x = 353, .y = 172, .w = 12, .h = 18 }, 54 | { .x = 367, .y = 172, .w = 12, .h = 18 }, 55 | }; 56 | 57 | const SDL_Rect Sprite_Numbers_Sm[10] = { 58 | { .x = 279, .y = 171, .w = 6, .h = 7 }, 59 | { .x = 282, .y = 180, .w = 3, .h = 7 }, 60 | { .x = 289, .y = 171, .w = 6, .h = 7 }, 61 | { .x = 289, .y = 180, .w = 6, .h = 7 }, 62 | { .x = 298, .y = 171, .w = 6, .h = 7 }, 63 | { .x = 298, .y = 180, .w = 6, .h = 7 }, 64 | { .x = 306, .y = 171, .w = 6, .h = 7 }, 65 | { .x = 306, .y = 180, .w = 6, .h = 7 }, 66 | { .x = 315, .y = 171, .w = 6, .h = 7 }, 67 | { .x = 315, .y = 180, .w = 6, .h = 7 }, 68 | }; 69 | 70 | const float Sprite_Scale = 7.0f; 71 | 72 | #define PLAYER_X (window_width / 5) 73 | 74 | const int Window_Width_Initial = 1170 * 0.5; 75 | const int Window_Height_Initial = 2532 * 0.5; 76 | 77 | int window_width_real; 78 | int window_height_real; 79 | 80 | float window_width_ratio; 81 | float window_height_ratio; 82 | 83 | const float Gravity = 3800.0f; 84 | const float Jump_Velocity_X = -1100.0f; 85 | const float Pipe_Velocity_X = -400.0f; 86 | 87 | const float Pipe_Gap = 380.0f; 88 | #define PIPE_SPACING (Sprite_Pipe.w * Sprite_Scale * 3.0f) 89 | 90 | SDL_Window *window; 91 | SDL_Renderer *renderer; 92 | int window_width = 0; 93 | int window_height = 0; 94 | SDL_Texture *texture; 95 | 96 | Mix_Chunk *sfx_die; 97 | Mix_Chunk *sfx_hit; 98 | Mix_Chunk *sfx_point; 99 | Mix_Chunk *sfx_swooshing; 100 | Mix_Chunk *sfx_wing; 101 | 102 | typedef enum game_state_t { 103 | STATE_MENU = 0, 104 | STATE_READY = 1, 105 | STATE_PLAY = 2, 106 | STATE_GAME_OVER = 3, 107 | } game_state_t; 108 | 109 | game_state_t game_state; 110 | void go_to_state(game_state_t state); 111 | 112 | void (*process_events)(); 113 | void (*update)(float dt); 114 | void (*render)(); 115 | 116 | int mouse_button_down = 0; 117 | SDL_FPoint mouse_pos; 118 | 119 | int menu_button_offset_y = 0; 120 | int game_over_button_offset_y = 0; 121 | 122 | float logo_offset = 0; // menu 123 | float ground_offset = 0; // menu, state, play 124 | 125 | int running; 126 | int game_over; 127 | uint64_t ticks; 128 | 129 | int score; 130 | int max_score; 131 | int new_max_score; 132 | 133 | float player_y; 134 | float player_velocity_y; 135 | 136 | const SDL_Rect *player_sprite; 137 | const SDL_Rect *medal_sprite; 138 | 139 | typedef struct pipe { 140 | float x; 141 | float gap_y; 142 | } pipe; 143 | 144 | #define Max_Pipes 10 145 | pipe pipes[Max_Pipes]; 146 | int pipes_len = 0; 147 | int pipe_to_pass = 0; 148 | 149 | int pause = 0; 150 | 151 | float game_over_flash_alpha; 152 | 153 | float clamp(float val, float min, float max) { 154 | const float t = val < min ? min : val; 155 | return t > max ? max : t; 156 | } 157 | 158 | int get_sprite_width(const SDL_Rect *rect) { 159 | return rect->w * Sprite_Scale; 160 | } 161 | 162 | int get_sprite_height(const SDL_Rect *rect) { 163 | return rect->h * Sprite_Scale; 164 | } 165 | 166 | SDL_Thread *save_thread; 167 | #define Save_File_Path_Len 256 168 | char save_file_path[Save_File_Path_Len]; 169 | 170 | int load_file() { 171 | FILE* f = fopen(save_file_path, "r"); 172 | if (!f) { 173 | if (errno == ENOENT) { 174 | printf("save file doesn't exist, max_score = 0\n"); 175 | return 0; 176 | } 177 | 178 | perror("load: failed to open file"); 179 | return 1; 180 | } 181 | 182 | if (fscanf(f, "%d\n", &max_score) < 0) { 183 | perror("load: failed to read from file"); 184 | fclose(f); 185 | return 1; 186 | } 187 | 188 | printf("save: successfully read from file. max_score = %d\n", max_score); 189 | fclose(f); 190 | 191 | return 0; 192 | } 193 | 194 | #define Save_File_Err_Len 128 195 | char save_file_err[Save_File_Err_Len]; 196 | 197 | typedef enum user_codes_t { 198 | USER_CODE_SAVE_SUCCESS = 0, 199 | USER_CODE_SAVE_ERROR = 1, 200 | } user_codes_t; 201 | 202 | int save_file(void *data) { 203 | SDL_Event event; 204 | 205 | event.type = SDL_USEREVENT; 206 | 207 | FILE* f = fopen(save_file_path, "w+"); 208 | if (!f) { 209 | event.user.code = USER_CODE_SAVE_ERROR; 210 | event.user.data1 = (void *)save_file_err; 211 | snprintf(save_file_err, Save_File_Err_Len, "failed to save: %s", strerror(errno)); 212 | SDL_PushEvent(&event); 213 | fclose(f); 214 | return 1; 215 | } 216 | 217 | if (fprintf(f, "%d\n", max_score) < 0) { 218 | event.user.code = USER_CODE_SAVE_ERROR; 219 | event.user.data1 = (void *)save_file_err; 220 | snprintf(save_file_err, Save_File_Err_Len, "failed to save: %s", strerror(errno)); 221 | SDL_PushEvent(&event); 222 | fclose(f); 223 | return 1; 224 | } 225 | 226 | event.user.code = USER_CODE_SAVE_SUCCESS; 227 | event.user.data1 = NULL; 228 | SDL_PushEvent(&event); 229 | fclose(f); 230 | 231 | return 0; 232 | } 233 | 234 | void reset() { 235 | game_over = 0; 236 | player_y = ((window_height) - (window_height / 8.0f)) / 2 - get_sprite_height(&Sprite_Players[0]) / 2; 237 | player_velocity_y = 0.0f; 238 | pipes_len = 0; 239 | score = 0; 240 | pipe_to_pass = 0; 241 | new_max_score = 0; 242 | } 243 | 244 | void jump() { 245 | Mix_PlayChannel(-1, sfx_wing, 0); 246 | player_velocity_y = Jump_Velocity_X; 247 | } 248 | 249 | int get_gap_y() { 250 | int min_y = ((window_height) - (window_height / 8.0f)) / 2 - Pipe_Gap / 2 - 400; 251 | int max_y = ((window_height) - (window_height / 8.0f)) / 2 - Pipe_Gap / 2 + 400; 252 | int range = max_y - min_y; 253 | return (rand() % range) + min_y; 254 | } 255 | 256 | void get_rect_background(SDL_FRect *rect) { 257 | rect->x = 0; 258 | rect->y = 0; 259 | rect->w = window_width; 260 | rect->h = window_height; 261 | } 262 | 263 | void get_rect_menu_logo(SDL_FRect *rect) { 264 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Logo) / 2 - get_sprite_width(player_sprite) / 2 - player_sprite->w; 265 | rect->y = 300 + logo_offset; 266 | rect->w = get_sprite_width(&Sprite_Logo); 267 | rect->h = get_sprite_height(&Sprite_Logo); 268 | } 269 | 270 | void get_rect_menu_player(SDL_FRect *rect) { 271 | rect->x = (window_width / 2) + get_sprite_width(&Sprite_Logo) / 2 - get_sprite_width(player_sprite) / 2 + player_sprite->w; 272 | rect->y = 300 + logo_offset + get_sprite_height(player_sprite) / 3; 273 | rect->w = get_sprite_width(player_sprite); 274 | rect->h = get_sprite_height(player_sprite); 275 | } 276 | 277 | void get_rect_menu_button(SDL_FRect *rect, int offset) { 278 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Button_Start) / 2; 279 | rect->y = (window_height / 4.0f) * 3.0f + offset; 280 | rect->w = get_sprite_width(&Sprite_Button_Start); 281 | rect->h = get_sprite_height(&Sprite_Button_Start); 282 | } 283 | 284 | void get_rect_ready_get_ready(SDL_FRect *rect) { 285 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Get_Ready) / 2; 286 | rect->y = (window_height / 4.0f); 287 | rect->w = get_sprite_width(&Sprite_Get_Ready); 288 | rect->h = get_sprite_height(&Sprite_Get_Ready); 289 | } 290 | 291 | void get_rect_ready_tap(SDL_FRect *rect) { 292 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Tap) / 2; 293 | rect->y = (window_height / 2) - get_sprite_height(&Sprite_Tap) / 2; 294 | rect->w = get_sprite_width(&Sprite_Tap); 295 | rect->h = get_sprite_height(&Sprite_Tap); 296 | } 297 | 298 | void get_rect_play_pause(SDL_FRect *rect) { 299 | rect->x = (window_width / 10); 300 | rect->y = (window_width / 10); 301 | rect->w = get_sprite_width(&Sprite_Button_Pause); 302 | rect->h = get_sprite_height(&Sprite_Button_Pause); 303 | } 304 | 305 | void get_rect_game_over(SDL_FRect *rect) { 306 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Game_Over) / 2; 307 | rect->y = (window_height / 4.0f); 308 | rect->w = get_sprite_width(&Sprite_Game_Over); 309 | rect->h = get_sprite_height(&Sprite_Game_Over); 310 | } 311 | 312 | void get_rect_game_over_board(SDL_FRect *rect) { 313 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Board) / 2; 314 | rect->y = (window_height / 2) - get_sprite_height(&Sprite_Board) / 2; 315 | rect->w = get_sprite_width(&Sprite_Board); 316 | rect->h = get_sprite_height(&Sprite_Board); 317 | } 318 | 319 | void get_rect_game_over_button(SDL_FRect *rect, int offset) { 320 | rect->x = (window_width / 2) - get_sprite_width(&Sprite_Button_OK) / 2; 321 | rect->y = (window_height / 4.0f) * 3.0f + offset; 322 | rect->w = get_sprite_width(&Sprite_Button_OK); 323 | rect->h = get_sprite_height(&Sprite_Button_OK); 324 | } 325 | 326 | void get_rect_player(SDL_FRect *rect) { 327 | rect->x = PLAYER_X; 328 | rect->y = player_y; 329 | rect->w = get_sprite_width(player_sprite); 330 | rect->h = get_sprite_height(player_sprite); 331 | } 332 | 333 | void get_rect_player_collider(SDL_FRect *rect) { 334 | rect->x = PLAYER_X + 1 * Sprite_Scale; 335 | rect->y = player_y + 1 * Sprite_Scale; 336 | rect->w = get_sprite_width(player_sprite) - 2 * Sprite_Scale; 337 | rect->h = get_sprite_height(player_sprite) - 2 * Sprite_Scale; 338 | } 339 | 340 | void get_pipe_top_rect(int i, SDL_FRect *rect) { 341 | rect->x = pipes[i].x; 342 | rect->y = -1000; 343 | rect->w = get_sprite_width(&Sprite_Pipe); 344 | rect->h = pipes[i].gap_y + 1000; 345 | } 346 | 347 | void get_pipe_top_end_rect(int i, SDL_FRect *rect) { 348 | rect->x = pipes[i].x; 349 | rect->y = pipes[i].gap_y - (Sprite_Pipe_Top.h * Sprite_Scale); 350 | rect->w = get_sprite_width(&Sprite_Pipe); 351 | rect->h = (Sprite_Pipe_Top.h * Sprite_Scale); 352 | } 353 | 354 | void get_pipe_bottom_rect(int i, SDL_FRect *rect) { 355 | rect->x = pipes[i].x; 356 | rect->y = pipes[i].gap_y + Pipe_Gap; 357 | rect->w = get_sprite_width(&Sprite_Pipe); 358 | rect->h = window_height - (window_height / 8.0f) - (pipes[i].gap_y + Pipe_Gap); 359 | } 360 | 361 | void get_pipe_bottom_end_rect(int i, SDL_FRect *rect) { 362 | rect->x = pipes[i].x; 363 | rect->y = pipes[i].gap_y + Pipe_Gap; 364 | rect->w = get_sprite_width(&Sprite_Pipe); 365 | rect->h = (Sprite_Pipe_Top.h * Sprite_Scale); 366 | } 367 | 368 | void get_top_rect(SDL_FRect *rect) { 369 | rect->x = 0; 370 | rect->y = -100; 371 | rect->w = window_width; 372 | rect->h = 100; 373 | } 374 | 375 | void get_bottom_rect(SDL_FRect *rect) { 376 | rect->x = 0; 377 | rect->y = (window_height / 8.0f) * 7; 378 | rect->w = window_width; 379 | rect->h = (window_height / 8.0f) * 1; 380 | } 381 | 382 | void get_ground_segment_rect(float x, SDL_FRect *rect) { 383 | rect->x = x; 384 | rect->y = (window_height / 8.0f) * 7; 385 | rect->w = get_sprite_width(&Sprite_Ground); 386 | rect->h = (window_height / 8.0f) * 1; 387 | } 388 | 389 | void draw_ground(SDL_FRect *rect) { 390 | for (int i = 0; ; i++) { 391 | int ground_x = ground_offset + get_sprite_width(&Sprite_Ground) * i; 392 | if (ground_x > window_width) { 393 | break; 394 | } 395 | 396 | get_ground_segment_rect(ground_x, rect); 397 | SDL_RenderCopyF(renderer, texture, &Sprite_Ground, rect); 398 | } 399 | } 400 | 401 | void draw_score(SDL_FRect *rect) { 402 | int score_cache = score; 403 | int digit; 404 | int digit_width; 405 | int digit_height; 406 | int diff = 0; 407 | 408 | while (1) { 409 | digit = score_cache % 10; 410 | digit_width = get_sprite_width(&Sprite_Numbers[digit]); 411 | digit_height = get_sprite_height(&Sprite_Numbers[digit]); 412 | 413 | diff += digit_width + 6; 414 | 415 | score_cache /= 10; 416 | if (score_cache == 0) { 417 | break; 418 | } 419 | } 420 | 421 | score_cache = score; 422 | 423 | int placement_x = window_width / 2 + diff / 2; 424 | 425 | while (1) { 426 | digit = score_cache % 10; 427 | digit_width = get_sprite_width(&Sprite_Numbers[digit]); 428 | digit_height = get_sprite_height(&Sprite_Numbers[digit]); 429 | 430 | rect->x = placement_x - digit_width; 431 | rect->y = 200; 432 | rect->w = digit_width; 433 | rect->h = digit_height; 434 | SDL_RenderCopyF(renderer, texture, &Sprite_Numbers[digit], rect); 435 | 436 | score_cache /= 10; 437 | if (score_cache == 0) { 438 | break; 439 | } 440 | 441 | placement_x = placement_x - digit_width - 6; 442 | } 443 | } 444 | 445 | void draw_score_small(SDL_FRect *rect) { 446 | SDL_FRect board_rect; 447 | 448 | get_rect_game_over_board(&board_rect); 449 | 450 | int score_cache = score; 451 | int digit; 452 | int digit_width; 453 | int digit_height; 454 | 455 | int placement_x = board_rect.x + board_rect.w - 76; 456 | 457 | while (1) { 458 | digit = score_cache % 10; 459 | digit_width = get_sprite_width(&Sprite_Numbers_Sm[digit]); 460 | digit_height = get_sprite_height(&Sprite_Numbers_Sm[digit]); 461 | 462 | rect->x = placement_x - digit_width; 463 | rect->y = board_rect.y + 125; 464 | rect->w = digit_width; 465 | rect->h = digit_height; 466 | SDL_RenderCopyF(renderer, texture, &Sprite_Numbers_Sm[digit], rect); 467 | 468 | score_cache /= 10; 469 | if (score_cache == 0) { 470 | break; 471 | } 472 | 473 | placement_x = placement_x - digit_width - 6; 474 | } 475 | } 476 | 477 | void draw_max_score_small(SDL_FRect *rect) { 478 | SDL_FRect board_rect; 479 | get_rect_game_over_board(&board_rect); 480 | 481 | int score_cache = max_score; 482 | int digit; 483 | int digit_width; 484 | int digit_height; 485 | 486 | int placement_x = board_rect.x + board_rect.w - 76; 487 | 488 | while (1) { 489 | digit = score_cache % 10; 490 | digit_width = get_sprite_width(&Sprite_Numbers_Sm[digit]); 491 | digit_height = get_sprite_height(&Sprite_Numbers_Sm[digit]); 492 | 493 | rect->x = placement_x - digit_width; 494 | rect->y = board_rect.y + 270; 495 | rect->w = digit_width; 496 | rect->h = digit_height; 497 | SDL_RenderCopyF(renderer, texture, &Sprite_Numbers_Sm[digit], rect); 498 | 499 | placement_x = placement_x - digit_width - 6; 500 | 501 | score_cache /= 10; 502 | if (score_cache == 0) { 503 | break; 504 | } 505 | } 506 | 507 | if (new_max_score) { 508 | rect->x = board_rect.x + 460; 509 | rect->y = board_rect.y + 204; 510 | rect->w = get_sprite_width(&Sprite_New); 511 | rect->h = get_sprite_height(&Sprite_New); 512 | SDL_RenderCopyF(renderer, texture, &Sprite_New, rect); 513 | } 514 | } 515 | 516 | void draw_medal(SDL_FRect *rect) { 517 | if (medal_sprite == NULL) { 518 | return; 519 | } 520 | 521 | SDL_FRect board_rect; 522 | get_rect_game_over_board(&board_rect); 523 | 524 | rect->x = board_rect.x + 94; 525 | rect->y = board_rect.y + 148; 526 | rect->w = get_sprite_width(medal_sprite); 527 | rect->h = get_sprite_height(medal_sprite); 528 | SDL_RenderCopyF(renderer, texture, medal_sprite, rect); 529 | } 530 | 531 | void get_mouse_state() { 532 | int x, y; 533 | mouse_button_down = SDL_GetMouseState(&x, &y) & SDL_BUTTON_LMASK; 534 | mouse_pos.x = x * window_width_ratio; 535 | mouse_pos.y = y * window_height_ratio; 536 | } 537 | 538 | int mouse_is_in_rect(const SDL_FRect *rect) { 539 | get_mouse_state(); 540 | return SDL_PointInFRect(&mouse_pos, rect); 541 | } 542 | 543 | void process_events_menu() { 544 | SDL_Event event; 545 | 546 | while (SDL_PollEvent(&event)) { 547 | switch (event.type) { 548 | case SDL_QUIT: 549 | running = 0; 550 | break; 551 | case SDL_KEYDOWN: 552 | if (event.key.repeat != 0) { 553 | break; 554 | } 555 | 556 | switch (event.key.keysym.sym) { 557 | case SDLK_ESCAPE: 558 | running = 0; 559 | break; 560 | case SDLK_SPACE: 561 | go_to_state(STATE_READY); 562 | reset(); 563 | default: 564 | break; 565 | } 566 | break; 567 | case SDL_MOUSEMOTION: 568 | get_mouse_state(); 569 | break; 570 | case SDL_MOUSEBUTTONUP: { 571 | SDL_FRect rect; 572 | get_rect_menu_button(&rect, 0); 573 | if (mouse_is_in_rect(&rect)) { 574 | go_to_state(STATE_READY); 575 | } 576 | break; 577 | } 578 | case SDL_WINDOWEVENT: 579 | SDL_GetRendererOutputSize(renderer, &window_width, &window_height); 580 | break; 581 | default: 582 | break; 583 | } 584 | } 585 | } 586 | 587 | void process_events_ready() { 588 | SDL_Event event; 589 | 590 | while (SDL_PollEvent(&event)) { 591 | switch (event.type) { 592 | case SDL_QUIT: 593 | running = 0; 594 | break; 595 | case SDL_KEYDOWN: 596 | if (event.key.repeat != 0) { 597 | break; 598 | } 599 | 600 | switch (event.key.keysym.sym) { 601 | case SDLK_ESCAPE: 602 | running = 0; 603 | break; 604 | case SDLK_SPACE: 605 | go_to_state(STATE_PLAY); 606 | break; 607 | default: 608 | break; 609 | } 610 | break; 611 | case SDL_MOUSEMOTION: 612 | get_mouse_state(); 613 | break; 614 | case SDL_MOUSEBUTTONDOWN: 615 | go_to_state(STATE_PLAY); 616 | break; 617 | case SDL_WINDOWEVENT: 618 | SDL_GetRendererOutputSize(renderer, &window_width, &window_height); 619 | break; 620 | default: 621 | break; 622 | } 623 | } 624 | } 625 | 626 | void process_events_play() { 627 | SDL_Event event; 628 | 629 | while (SDL_PollEvent(&event)) { 630 | switch (event.type) { 631 | case SDL_QUIT: 632 | running = 0; 633 | break; 634 | case SDL_KEYDOWN: 635 | if (event.key.repeat != 0) { 636 | break; 637 | } 638 | 639 | switch (event.key.keysym.sym) { 640 | case SDLK_ESCAPE: 641 | running = 0; 642 | break; 643 | case SDLK_SPACE: 644 | jump(); 645 | break; 646 | default: 647 | break; 648 | } 649 | break; 650 | case SDL_MOUSEMOTION: 651 | get_mouse_state(); 652 | break; 653 | case SDL_MOUSEBUTTONDOWN: { 654 | SDL_FRect rect; 655 | get_rect_play_pause(&rect); 656 | if (mouse_is_in_rect(&rect)) { 657 | pause = !pause; 658 | break; 659 | } 660 | 661 | if (!pause) { 662 | jump(); 663 | } 664 | 665 | break; 666 | } 667 | case SDL_WINDOWEVENT: 668 | SDL_GetRendererOutputSize(renderer, &window_width, &window_height); 669 | break; 670 | default: 671 | break; 672 | } 673 | } 674 | } 675 | 676 | void process_events_game_over() { 677 | SDL_Event event; 678 | 679 | while (SDL_PollEvent(&event)) { 680 | switch (event.type) { 681 | case SDL_QUIT: 682 | running = 0; 683 | break; 684 | case SDL_KEYDOWN: 685 | if (event.key.repeat != 0) { 686 | break; 687 | } 688 | 689 | switch (event.key.keysym.sym) { 690 | case SDLK_ESCAPE: 691 | running = 0; 692 | break; 693 | case SDLK_SPACE: 694 | go_to_state(STATE_MENU); 695 | break; 696 | default: 697 | break; 698 | } 699 | break; 700 | case SDL_MOUSEMOTION: 701 | get_mouse_state(); 702 | break; 703 | case SDL_MOUSEBUTTONUP: { 704 | SDL_FRect rect; 705 | get_rect_game_over_button(&rect, 0); 706 | if (mouse_is_in_rect(&rect)) { 707 | go_to_state(STATE_MENU); 708 | } 709 | break; 710 | } 711 | case SDL_WINDOWEVENT: 712 | SDL_GetRendererOutputSize(renderer, &window_width, &window_height); 713 | break; 714 | case SDL_USEREVENT: 715 | switch (event.user.code) { 716 | case USER_CODE_SAVE_SUCCESS: 717 | printf("successful file save, max_score = %d\n", max_score); 718 | break; 719 | case USER_CODE_SAVE_ERROR: 720 | printf("error saving file: %s\n", (const char *)event.user.data1); 721 | break; 722 | default: 723 | break; 724 | } 725 | default: 726 | break; 727 | } 728 | } 729 | } 730 | 731 | void update_menu(float dt) { 732 | logo_offset = sin(ticks / 200.0) * 10; 733 | 734 | player_sprite = &Sprite_Players[ticks % 300 / 100]; 735 | 736 | ground_offset += Pipe_Velocity_X * dt; 737 | ground_offset = fmodf(ground_offset, Sprite_Ground.w * Sprite_Scale); 738 | 739 | SDL_FRect rect; 740 | get_rect_menu_button(&rect, 0); 741 | menu_button_offset_y = mouse_is_in_rect(&rect) * mouse_button_down * 1 * Sprite_Scale; 742 | } 743 | 744 | void update_ready(float dt) { 745 | logo_offset = sin(ticks / 200.0) * 10; 746 | 747 | player_sprite = &Sprite_Players[ticks % 300 / 100]; 748 | 749 | ground_offset += Pipe_Velocity_X * dt; 750 | ground_offset = fmodf(ground_offset, Sprite_Ground.w * Sprite_Scale); 751 | } 752 | 753 | void update_play(float dt) { 754 | if (pause) { 755 | return; 756 | } 757 | 758 | ground_offset += Pipe_Velocity_X * dt; 759 | ground_offset = fmodf(ground_offset, Sprite_Ground.w * Sprite_Scale); 760 | 761 | // If there are no pipes, create a pipe 762 | if (pipes_len == 0) { 763 | pipes[0].x = window_width; 764 | pipes[0].gap_y = get_gap_y(); 765 | pipes_len++; 766 | } 767 | 768 | // If the last pipe is futher away than PIPE_SPACING, create a new one 769 | if (pipes_len < Max_Pipes && pipes[pipes_len - 1].x < window_width - PIPE_SPACING) { 770 | pipes[pipes_len].x = window_width; 771 | pipes[pipes_len].gap_y = get_gap_y(); 772 | pipes_len++; 773 | } 774 | 775 | // Remove pipes than the player has passed 776 | while (pipes[0].x + get_sprite_width(&Sprite_Pipe) < 0) { 777 | // Move pipes down in the array 778 | for (int i = 0; i < pipes_len - 1; i++) { 779 | pipes[i] = pipes[i + 1]; 780 | } 781 | pipes_len--; 782 | pipe_to_pass--; 783 | } 784 | 785 | for (int i = 0; i < pipes_len; i++) { 786 | pipes[i].x += Pipe_Velocity_X * dt; 787 | } 788 | 789 | player_velocity_y += Gravity * dt; 790 | player_y += player_velocity_y * dt; 791 | player_sprite = &Sprite_Players[ticks % 300 / 100]; 792 | 793 | #if COLLISION_DETECTION 794 | // Collision detection 795 | SDL_FRect a; 796 | SDL_FRect b; 797 | 798 | get_rect_player_collider(&a); 799 | 800 | get_bottom_rect(&b); 801 | if (SDL_HasIntersectionF(&a, &b)) { 802 | go_to_state(STATE_GAME_OVER); 803 | return; 804 | } 805 | 806 | for (int i = 0; i < pipes_len; i++) { 807 | get_pipe_top_rect(i, &b); 808 | if (SDL_HasIntersectionF(&a, &b)) { 809 | go_to_state(STATE_GAME_OVER); 810 | return; 811 | } 812 | 813 | get_pipe_bottom_rect(i, &b); 814 | if (SDL_HasIntersectionF(&a, &b)) { 815 | go_to_state(STATE_GAME_OVER); 816 | return; 817 | } 818 | } 819 | #endif // COLLISION_DETECTION 820 | 821 | if (PLAYER_X > pipes[pipe_to_pass].x) { 822 | Mix_PlayChannel(-1, sfx_point, 0); 823 | score++; 824 | pipe_to_pass++; 825 | } 826 | } 827 | 828 | void update_game_over(float dt) { 829 | SDL_FRect rect; 830 | get_rect_game_over_button(&rect, 0); 831 | game_over_button_offset_y = mouse_is_in_rect(&rect) * mouse_button_down * 1 * Sprite_Scale; 832 | game_over_flash_alpha = clamp(game_over_flash_alpha -= dt * 500, 0, 255); 833 | } 834 | 835 | void render_menu() { 836 | SDL_FRect rect; 837 | 838 | get_rect_background(&rect); 839 | SDL_RenderCopyF(renderer, texture, &Sprite_Background, &rect); 840 | 841 | get_rect_menu_logo(&rect); 842 | SDL_RenderCopyF(renderer, texture, &Sprite_Logo, &rect); 843 | 844 | get_rect_menu_player(&rect); 845 | SDL_RenderCopyF(renderer, texture, player_sprite, &rect); 846 | 847 | draw_ground(&rect); 848 | 849 | get_rect_menu_button(&rect, menu_button_offset_y); 850 | SDL_RenderCopyF(renderer, texture, &Sprite_Button_Start, &rect); 851 | 852 | SDL_RenderPresent(renderer); 853 | } 854 | 855 | void render_ready() { 856 | SDL_FRect rect; 857 | 858 | get_rect_background(&rect); 859 | SDL_RenderCopyF(renderer, texture, &Sprite_Background, &rect); 860 | 861 | get_rect_ready_get_ready(&rect); 862 | SDL_RenderCopyF(renderer, texture, &Sprite_Get_Ready, &rect); 863 | 864 | get_rect_player(&rect); 865 | SDL_RenderCopyF(renderer, texture, player_sprite, &rect); 866 | 867 | draw_ground(&rect); 868 | 869 | get_rect_ready_tap(&rect); 870 | SDL_RenderCopyF(renderer, texture, &Sprite_Tap, &rect); 871 | 872 | draw_score(&rect); 873 | 874 | SDL_RenderPresent(renderer); 875 | } 876 | 877 | void render_play() { 878 | SDL_FRect rect; 879 | 880 | get_rect_background(&rect); 881 | SDL_RenderCopyF(renderer, texture, &Sprite_Background, &rect); 882 | 883 | draw_ground(&rect); 884 | 885 | for (int i = 0; i < pipes_len; i++) { 886 | get_pipe_top_rect(i, &rect); 887 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe, &rect); 888 | 889 | get_pipe_top_end_rect(i, &rect); 890 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe_Top, &rect); 891 | 892 | get_pipe_bottom_rect(i, &rect); 893 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe, &rect); 894 | 895 | get_pipe_bottom_end_rect(i, &rect); 896 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe_Bottom, &rect); 897 | } 898 | 899 | #if DRAW_PLAYER_COLLIDER 900 | get_rect_player_collider(&rect); 901 | SDL_SetRenderDrawColor(renderer, 200, 100, 100, 100); 902 | SDL_RenderFillRectF(renderer, &rect); 903 | #endif // DRAW_PLAYER_COLLIDER 904 | 905 | get_rect_player(&rect); 906 | SDL_RenderCopyF(renderer, texture, player_sprite, &rect); 907 | 908 | get_rect_play_pause(&rect); 909 | SDL_RenderCopyF(renderer, texture, pause ? &Sprite_Button_Play : &Sprite_Button_Pause, &rect); 910 | 911 | draw_score(&rect); 912 | 913 | SDL_RenderPresent(renderer); 914 | } 915 | 916 | void render_game_over() { 917 | SDL_FRect rect; 918 | 919 | get_rect_background(&rect); 920 | SDL_RenderCopyF(renderer, texture, &Sprite_Background, &rect); 921 | 922 | draw_ground(&rect); 923 | 924 | for (int i = 0; i < pipes_len; i++) { 925 | get_pipe_top_rect(i, &rect); 926 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe, &rect); 927 | 928 | get_pipe_top_end_rect(i, &rect); 929 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe_Top, &rect); 930 | 931 | get_pipe_bottom_rect(i, &rect); 932 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe, &rect); 933 | 934 | get_pipe_bottom_end_rect(i, &rect); 935 | SDL_RenderCopyF(renderer, texture, &Sprite_Pipe_Bottom, &rect); 936 | } 937 | 938 | #if DRAW_PLAYER_COLLIDER 939 | get_rect_player_collider(&rect); 940 | SDL_SetRenderDrawColor(renderer, 200, 100, 100, 100); 941 | SDL_RenderFillRectF(renderer, &rect); 942 | #endif // DRAW_PLAYER_COLLIDER 943 | 944 | get_rect_player(&rect); 945 | SDL_RenderCopyF(renderer, texture, player_sprite, &rect); 946 | 947 | draw_score(&rect); 948 | 949 | get_rect_game_over(&rect); 950 | SDL_RenderCopyF(renderer, texture, &Sprite_Game_Over, &rect); 951 | 952 | get_rect_game_over_board(&rect); 953 | SDL_RenderCopyF(renderer, texture, &Sprite_Board, &rect); 954 | 955 | draw_score_small(&rect); 956 | draw_max_score_small(&rect); 957 | draw_medal(&rect); 958 | 959 | get_rect_game_over_button(&rect, game_over_button_offset_y); 960 | SDL_RenderCopyF(renderer, texture, &Sprite_Button_OK, &rect); 961 | 962 | rect.x = 0; 963 | rect.y = 0; 964 | rect.w = window_width; 965 | rect.h = window_height; 966 | 967 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, game_over_flash_alpha); 968 | SDL_RenderFillRectF(renderer, &rect); 969 | 970 | SDL_RenderPresent(renderer); 971 | } 972 | 973 | void go_to_state(game_state_t state) { 974 | game_state = state; 975 | 976 | switch (state) { 977 | case STATE_MENU: 978 | process_events = process_events_menu; 979 | update = update_menu; 980 | render = render_menu; 981 | reset(); 982 | Mix_PlayChannel(-1, sfx_swooshing, 0); 983 | break; 984 | case STATE_READY: 985 | process_events = process_events_ready; 986 | update = update_ready; 987 | render = render_ready; 988 | Mix_PlayChannel(-1, sfx_swooshing, 0); 989 | break; 990 | case STATE_PLAY: 991 | process_events = process_events_play; 992 | update = update_play; 993 | render = render_play; 994 | reset(); 995 | jump(); 996 | break; 997 | case STATE_GAME_OVER: { 998 | process_events = process_events_game_over; 999 | update = update_game_over; 1000 | render = render_game_over; 1001 | 1002 | Mix_PlayChannel(-1, sfx_hit, 0); 1003 | 1004 | if (score >= 40) { 1005 | medal_sprite = &Sprite_Medal_Platinum; 1006 | } else if (score >= 30) { 1007 | medal_sprite = &Sprite_Medal_Gold; 1008 | } else if (score >= 20) { 1009 | medal_sprite = &Sprite_Medal_Silver; 1010 | } else if (score >= 10) { 1011 | medal_sprite = &Sprite_Medal_Bronze; 1012 | } else { 1013 | medal_sprite = NULL; 1014 | } 1015 | 1016 | game_over_flash_alpha = 255; 1017 | 1018 | if (score <= max_score) { 1019 | break; 1020 | } 1021 | 1022 | new_max_score = 1; 1023 | max_score = score; 1024 | 1025 | save_thread = SDL_CreateThread(save_file, "save_file", NULL); 1026 | if (!save_thread) { 1027 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window); 1028 | } 1029 | SDL_DetachThread(save_thread); 1030 | 1031 | break; 1032 | } 1033 | default: 1034 | break; 1035 | } 1036 | } 1037 | 1038 | void run() { 1039 | running = 1; 1040 | uint64_t lastTicks = SDL_GetTicks64(); 1041 | 1042 | go_to_state(STATE_MENU); 1043 | 1044 | while (running) { 1045 | ticks = SDL_GetTicks64(); 1046 | float dt = (ticks - lastTicks) / 1000.0f; 1047 | lastTicks = ticks; 1048 | 1049 | process_events(); 1050 | update(dt); 1051 | render(); 1052 | } 1053 | } 1054 | 1055 | void show_error(const char* fmt, ...) { 1056 | const size_t errlen = 128; 1057 | char errstr[errlen]; 1058 | 1059 | va_list args; 1060 | va_start(args, fmt); 1061 | 1062 | vsnprintf(errstr, errlen, fmt, args); 1063 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", errstr, window); 1064 | 1065 | va_end(args); 1066 | } 1067 | 1068 | int main(int argc, char *argv[]) { 1069 | srand(time(NULL)); 1070 | const size_t errlen = 128; 1071 | char errstr[errlen]; 1072 | 1073 | #if defined(__IPHONEOS__) 1074 | snprintf(save_file_path, Save_File_Path_Len, "%s/Documents/data.txt", getenv("HOME")); 1075 | #else 1076 | snprintf(save_file_path, Save_File_Path_Len, "data.txt"); 1077 | #endif 1078 | 1079 | if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { 1080 | show_error("Error initialising SDL: %s", SDL_GetError()); 1081 | return 1; 1082 | } 1083 | 1084 | if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG) { 1085 | show_error("Error initialising SDL_image: %s", IMG_GetError()); 1086 | return 1; 1087 | } 1088 | 1089 | if (Mix_Init(0) != 0) { 1090 | show_error("Error initialising SDL_mixer: %s", Mix_GetError()); 1091 | return 1; 1092 | } 1093 | 1094 | window = SDL_CreateWindow("Flappy Bird", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, Window_Width_Initial, Window_Height_Initial, Window_Flags); 1095 | if (!window) { 1096 | show_error("Error creating window: %s", SDL_GetError()); 1097 | return 1; 1098 | } 1099 | 1100 | SDL_GetWindowSize(window, &window_width_real, &window_height_real); 1101 | 1102 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 1103 | if (!renderer) { 1104 | show_error("Error creating renderer: %s", SDL_GetError()); 1105 | return 1; 1106 | } 1107 | 1108 | if (SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND) < 0) { 1109 | show_error("Error setting renderer blend mode: %s", SDL_GetError()); 1110 | return 1; 1111 | }; 1112 | 1113 | if (SDL_GetRendererOutputSize(renderer, &window_width, &window_height) != 0) { 1114 | show_error("Error getting renderer output size: %s", SDL_GetError()); 1115 | return 1; 1116 | } 1117 | 1118 | window_width_ratio = (float)window_width / (float)window_width_real; 1119 | window_height_ratio = (float)window_height / (float)window_height_real; 1120 | 1121 | texture = IMG_LoadTexture(renderer, "spritesheet.png"); 1122 | if (!texture) { 1123 | show_error("Error loading sprite sheet: %s", IMG_GetError()); 1124 | return 1; 1125 | } 1126 | 1127 | if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) != 0) { 1128 | show_error("Error opening audio device: %s", Mix_GetError()); 1129 | return 1; 1130 | } 1131 | 1132 | sfx_die = Mix_LoadWAV("sfx_die.wav"); 1133 | if (!(sfx_die)) { 1134 | show_error(Mix_GetError()); 1135 | return 1; 1136 | } 1137 | 1138 | sfx_hit = Mix_LoadWAV("sfx_hit.wav"); 1139 | if (!sfx_hit) { 1140 | show_error(Mix_GetError()); 1141 | return 1; 1142 | } 1143 | 1144 | sfx_point = Mix_LoadWAV("sfx_point.wav"); 1145 | if (!sfx_point) { 1146 | show_error(Mix_GetError()); 1147 | return 1; 1148 | } 1149 | 1150 | sfx_swooshing = Mix_LoadWAV("sfx_swooshing.wav"); 1151 | if (!sfx_swooshing) { 1152 | show_error(Mix_GetError()); 1153 | return 1; 1154 | } 1155 | 1156 | sfx_wing = Mix_LoadWAV("sfx_wing.wav"); 1157 | if (!sfx_wing) { 1158 | show_error(Mix_GetError()); 1159 | return 1; 1160 | } 1161 | 1162 | if (load_file() != 0) { 1163 | show_error(strerror(errno)); 1164 | return 1; 1165 | } 1166 | 1167 | run(); 1168 | 1169 | Mix_FreeChunk(sfx_wing); 1170 | Mix_FreeChunk(sfx_swooshing); 1171 | Mix_FreeChunk(sfx_point); 1172 | Mix_FreeChunk(sfx_hit); 1173 | Mix_FreeChunk(sfx_die); 1174 | Mix_CloseAudio(); 1175 | 1176 | SDL_DestroyTexture(texture); 1177 | SDL_DestroyRenderer(renderer); 1178 | SDL_DestroyWindow(window); 1179 | 1180 | Mix_Quit(); 1181 | IMG_Quit(); 1182 | SDL_Quit(); 1183 | 1184 | return 0; 1185 | } 1186 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/icon.png -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/preview.gif -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/screenshot.png -------------------------------------------------------------------------------- /sfx_die.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/sfx_die.wav -------------------------------------------------------------------------------- /sfx_hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/sfx_hit.wav -------------------------------------------------------------------------------- /sfx_point.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/sfx_point.wav -------------------------------------------------------------------------------- /sfx_swooshing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/sfx_swooshing.wav -------------------------------------------------------------------------------- /sfx_wing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/sfx_wing.wav -------------------------------------------------------------------------------- /spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alxyng/flappybird/3e494fd3461d87e58a1741a303a03fcfa60c5c1c/spritesheet.png --------------------------------------------------------------------------------