├── .gitignore ├── README.md ├── assets ├── fonts │ ├── Marker Felt.ttf │ ├── futura-48.fnt │ └── futura-48.png ├── images │ ├── bird.json │ └── bird.png └── sound │ ├── collision.ogg │ ├── drop.ogg │ ├── point.ogg │ ├── rise.ogg │ └── swooshing.ogg ├── game.go ├── images └── flappybird.final.gif ├── main.go └── pipe.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flappy Bird 2 | 3 | This is a Game made with Go and Korok. [korok.io](https://korok.io) 4 | 5 | ![flappy bird](https://github.com/ntop001/flappybird/blob/master/images/flappybird.final.gif) 6 | -------------------------------------------------------------------------------- /assets/fonts/Marker Felt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/fonts/Marker Felt.ttf -------------------------------------------------------------------------------- /assets/fonts/futura-48.fnt: -------------------------------------------------------------------------------- 1 | info face="Futura-CondensedExtraBold" size=48 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=2,2 2 | common lineHeight=62 base=48 scaleW=512 scaleH=256 pages=1 packed=0 3 | page id=0 file="futura-48.png" 4 | chars count=95 5 | char id=92 x=2 y=2 width=35 height=56 xoffset=0 yoffset=7 xadvance=32 page=0 chnl=0 letter="\" 6 | char id=47 x=39 y=2 width=35 height=56 xoffset=-1 yoffset=7 xadvance=32 page=0 chnl=0 letter="/" 7 | char id=125 x=76 y=2 width=22 height=56 xoffset=-1 yoffset=7 xadvance=19 page=0 chnl=0 letter="}" 8 | char id=123 x=100 y=2 width=21 height=56 xoffset=1 yoffset=7 xadvance=19 page=0 chnl=0 letter="{" 9 | char id=40 x=123 y=2 width=19 height=56 xoffset=1 yoffset=7 xadvance=18 page=0 chnl=0 letter="(" 10 | char id=41 x=144 y=2 width=19 height=56 xoffset=0 yoffset=7 xadvance=18 page=0 chnl=0 letter=")" 11 | char id=91 x=165 y=2 width=19 height=56 xoffset=2 yoffset=7 xadvance=18 page=0 chnl=0 letter="[" 12 | char id=93 x=186 y=2 width=19 height=56 xoffset=0 yoffset=7 xadvance=18 page=0 chnl=0 letter="]" 13 | char id=106 x=207 y=2 width=16 height=55 xoffset=0 yoffset=8 xadvance=13 page=0 chnl=0 letter="j" 14 | char id=36 x=225 y=2 width=24 height=52 xoffset=3 yoffset=5 xadvance=28 page=0 chnl=0 letter="$" 15 | char id=124 x=251 y=2 width=15 height=50 xoffset=7 yoffset=10 xadvance=27 page=0 chnl=0 letter="|" 16 | char id=81 x=268 y=2 width=33 height=46 xoffset=0 yoffset=10 xadvance=31 page=0 chnl=0 letter="Q" 17 | char id=100 x=303 y=2 width=28 height=45 xoffset=0 yoffset=7 xadvance=26 page=0 chnl=0 letter="d" 18 | char id=98 x=333 y=2 width=28 height=45 xoffset=1 yoffset=7 xadvance=26 page=0 chnl=0 letter="b" 19 | char id=107 x=363 y=2 width=29 height=44 xoffset=1 yoffset=7 xadvance=26 page=0 chnl=0 letter="k" 20 | char id=63 x=394 y=2 width=28 height=44 xoffset=1 yoffset=8 xadvance=27 page=0 chnl=0 letter="?" 21 | char id=104 x=424 y=2 width=27 height=44 xoffset=1 yoffset=7 xadvance=26 page=0 chnl=0 letter="h" 22 | char id=102 x=453 y=2 width=21 height=44 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=0 letter="f" 23 | char id=33 x=476 y=2 width=16 height=44 xoffset=2 yoffset=8 xadvance=17 page=0 chnl=0 letter="!" 24 | char id=108 x=494 y=2 width=14 height=44 xoffset=1 yoffset=7 xadvance=13 page=0 chnl=0 letter="l" 25 | char id=37 x=2 y=60 width=39 height=43 xoffset=0 yoffset=9 xadvance=37 page=0 chnl=0 letter="%" 26 | char id=105 x=43 y=60 width=16 height=43 xoffset=0 yoffset=8 xadvance=13 page=0 chnl=0 letter="i" 27 | char id=64 x=61 y=60 width=38 height=42 xoffset=1 yoffset=10 xadvance=37 page=0 chnl=0 letter="@" 28 | char id=38 x=101 y=60 width=36 height=42 xoffset=1 yoffset=10 xadvance=34 page=0 chnl=0 letter="&" 29 | char id=79 x=139 y=60 width=33 height=42 xoffset=0 yoffset=10 xadvance=30 page=0 chnl=0 letter="O" 30 | char id=71 x=174 y=60 width=32 height=42 xoffset=0 yoffset=10 xadvance=30 page=0 chnl=0 letter="G" 31 | char id=48 x=208 y=60 width=31 height=42 xoffset=0 yoffset=10 xadvance=28 page=0 chnl=0 letter="0" 32 | char id=85 x=241 y=60 width=30 height=42 xoffset=1 yoffset=10 xadvance=29 page=0 chnl=0 letter="U" 33 | char id=51 x=273 y=60 width=29 height=42 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="3" 34 | char id=54 x=304 y=60 width=29 height=42 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="6" 35 | char id=53 x=335 y=60 width=29 height=42 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="5" 36 | char id=56 x=366 y=60 width=29 height=42 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="8" 37 | char id=103 x=397 y=60 width=28 height=42 xoffset=0 yoffset=21 xadvance=26 page=0 chnl=0 letter="g" 38 | char id=112 x=427 y=60 width=28 height=42 xoffset=1 yoffset=21 xadvance=26 page=0 chnl=0 letter="p" 39 | char id=113 x=457 y=60 width=28 height=42 xoffset=0 yoffset=21 xadvance=26 page=0 chnl=0 letter="q" 40 | char id=67 x=2 y=105 width=26 height=42 xoffset=0 yoffset=10 xadvance=24 page=0 chnl=0 letter="C" 41 | char id=83 x=30 y=105 width=24 height=42 xoffset=0 yoffset=10 xadvance=21 page=0 chnl=0 letter="S" 42 | char id=74 x=56 y=105 width=21 height=42 xoffset=-1 yoffset=10 xadvance=19 page=0 chnl=0 letter="J" 43 | char id=87 x=79 y=105 width=45 height=41 xoffset=-1 yoffset=10 xadvance=40 page=0 chnl=0 letter="W" 44 | char id=77 x=126 y=105 width=43 height=41 xoffset=0 yoffset=10 xadvance=41 page=0 chnl=0 letter="M" 45 | char id=65 x=171 y=105 width=33 height=41 xoffset=-1 yoffset=10 xadvance=28 page=0 chnl=0 letter="A" 46 | char id=86 x=206 y=105 width=32 height=41 xoffset=-1 yoffset=10 xadvance=28 page=0 chnl=0 letter="V" 47 | char id=88 x=240 y=105 width=32 height=41 xoffset=-1 yoffset=10 xadvance=28 page=0 chnl=0 letter="X" 48 | char id=90 x=274 y=105 width=32 height=41 xoffset=-1 yoffset=10 xadvance=27 page=0 chnl=0 letter="Z" 49 | char id=89 x=308 y=105 width=31 height=41 xoffset=-1 yoffset=10 xadvance=27 page=0 chnl=0 letter="Y" 50 | char id=52 x=341 y=105 width=31 height=41 xoffset=0 yoffset=10 xadvance=28 page=0 chnl=0 letter="4" 51 | char id=75 x=374 y=105 width=31 height=41 xoffset=1 yoffset=10 xadvance=29 page=0 chnl=0 letter="K" 52 | char id=82 x=407 y=105 width=31 height=41 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="R" 53 | char id=55 x=440 y=105 width=31 height=41 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="7" 54 | char id=68 x=473 y=105 width=30 height=41 xoffset=1 yoffset=10 xadvance=29 page=0 chnl=0 letter="D" 55 | char id=78 x=2 y=149 width=30 height=41 xoffset=1 yoffset=10 xadvance=30 page=0 chnl=0 letter="N" 56 | char id=50 x=34 y=149 width=30 height=41 xoffset=0 yoffset=10 xadvance=28 page=0 chnl=0 letter="2" 57 | char id=66 x=66 y=149 width=30 height=41 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="B" 58 | char id=72 x=98 y=149 width=29 height=41 xoffset=1 yoffset=10 xadvance=29 page=0 chnl=0 letter="H" 59 | char id=80 x=129 y=149 width=29 height=41 xoffset=1 yoffset=10 xadvance=27 page=0 chnl=0 letter="P" 60 | char id=57 x=160 y=149 width=29 height=41 xoffset=1 yoffset=10 xadvance=28 page=0 chnl=0 letter="9" 61 | char id=121 x=191 y=149 width=29 height=41 xoffset=-1 yoffset=21 xadvance=24 page=0 chnl=0 letter="y" 62 | char id=35 x=222 y=149 width=27 height=41 xoffset=2 yoffset=10 xadvance=28 page=0 chnl=0 letter="#" 63 | char id=84 x=251 y=149 width=25 height=41 xoffset=-1 yoffset=10 xadvance=20 page=0 chnl=0 letter="T" 64 | char id=69 x=278 y=149 width=23 height=41 xoffset=1 yoffset=10 xadvance=22 page=0 chnl=0 letter="E" 65 | char id=70 x=303 y=149 width=22 height=41 xoffset=1 yoffset=10 xadvance=21 page=0 chnl=0 letter="F" 66 | char id=76 x=327 y=149 width=22 height=41 xoffset=1 yoffset=10 xadvance=20 page=0 chnl=0 letter="L" 67 | char id=49 x=351 y=149 width=18 height=41 xoffset=6 yoffset=10 xadvance=28 page=0 chnl=0 letter="1" 68 | char id=73 x=371 y=149 width=15 height=41 xoffset=1 yoffset=10 xadvance=14 page=0 chnl=0 letter="I" 69 | char id=60 x=388 y=149 width=29 height=39 xoffset=1 yoffset=11 xadvance=28 page=0 chnl=0 letter="<" 70 | char id=62 x=419 y=149 width=29 height=39 xoffset=1 yoffset=11 xadvance=28 page=0 chnl=0 letter=">" 71 | char id=59 x=450 y=149 width=20 height=39 xoffset=-2 yoffset=21 xadvance=14 page=0 chnl=0 letter=";" 72 | char id=116 x=472 y=149 width=20 height=37 xoffset=-1 yoffset=14 xadvance=16 page=0 chnl=0 letter="t" 73 | char id=111 x=2 y=192 width=28 height=31 xoffset=0 yoffset=21 xadvance=26 page=0 chnl=0 letter="o" 74 | char id=97 x=32 y=192 width=28 height=31 xoffset=0 yoffset=21 xadvance=26 page=0 chnl=0 letter="a" 75 | char id=101 x=62 y=192 width=28 height=31 xoffset=0 yoffset=21 xadvance=26 page=0 chnl=0 letter="e" 76 | char id=117 x=92 y=192 width=27 height=31 xoffset=1 yoffset=21 xadvance=27 page=0 chnl=0 letter="u" 77 | char id=115 x=121 y=192 width=24 height=31 xoffset=0 yoffset=21 xadvance=21 page=0 chnl=0 letter="s" 78 | char id=99 x=147 y=192 width=19 height=31 xoffset=0 yoffset=21 xadvance=16 page=0 chnl=0 letter="c" 79 | char id=58 x=168 y=192 width=16 height=31 xoffset=0 yoffset=21 xadvance=14 page=0 chnl=0 letter=":" 80 | char id=119 x=186 y=192 width=41 height=30 xoffset=-1 yoffset=21 xadvance=37 page=0 chnl=0 letter="w" 81 | char id=109 x=229 y=192 width=40 height=30 xoffset=1 yoffset=21 xadvance=39 page=0 chnl=0 letter="m" 82 | char id=120 x=271 y=192 width=30 height=30 xoffset=-1 yoffset=21 xadvance=26 page=0 chnl=0 letter="x" 83 | char id=118 x=303 y=192 width=29 height=30 xoffset=-1 yoffset=21 xadvance=24 page=0 chnl=0 letter="v" 84 | char id=110 x=334 y=192 width=27 height=30 xoffset=1 yoffset=21 xadvance=26 page=0 chnl=0 letter="n" 85 | char id=122 x=363 y=192 width=26 height=30 xoffset=0 yoffset=21 xadvance=23 page=0 chnl=0 letter="z" 86 | char id=114 x=391 y=192 width=21 height=30 xoffset=1 yoffset=21 xadvance=18 page=0 chnl=0 letter="r" 87 | char id=43 x=414 y=192 width=29 height=29 xoffset=1 yoffset=16 xadvance=28 page=0 chnl=0 letter="+" 88 | char id=42 x=445 y=192 width=25 height=24 xoffset=3 yoffset=7 xadvance=28 page=0 chnl=0 letter="*" 89 | char id=94 x=472 y=192 width=31 height=23 xoffset=-1 yoffset=10 xadvance=27 page=0 chnl=0 letter="^" 90 | char id=44 x=2 y=225 width=19 height=22 xoffset=-2 yoffset=38 xadvance=14 page=0 chnl=0 letter="," 91 | char id=34 x=23 y=225 width=24 height=20 xoffset=0 yoffset=10 xadvance=22 page=0 chnl=0 letter=""" 92 | char id=39 x=49 y=225 width=14 height=20 xoffset=0 yoffset=10 xadvance=12 page=0 chnl=0 letter="'" 93 | char id=61 x=65 y=225 width=29 height=19 xoffset=1 yoffset=21 xadvance=28 page=0 chnl=0 letter="=" 94 | char id=46 x=96 y=225 width=16 height=16 xoffset=0 yoffset=36 xadvance=14 page=0 chnl=0 letter="." 95 | char id=126 x=114 y=225 width=31 height=15 xoffset=4 yoffset=6 xadvance=36 page=0 chnl=0 letter="~" 96 | char id=96 x=147 y=225 width=15 height=13 xoffset=5 yoffset=10 xadvance=27 page=0 chnl=0 letter="`" 97 | char id=45 x=164 y=225 width=16 height=12 xoffset=-1 yoffset=30 xadvance=11 page=0 chnl=0 letter="-" 98 | char id=95 x=182 y=225 width=32 height=11 xoffset=-1 yoffset=49 xadvance=27 page=0 chnl=0 letter="_" 99 | char id=32 x=216 y=225 width=0 height=0 xoffset=-106 yoffset=168 xadvance=14 page=0 chnl=0 letter="space" 100 | kernings count=82 101 | kerning first=81 second=81 amount=-1 102 | kerning first=63 second=93 amount=-2 103 | kerning first=63 second=100 amount=-3 104 | kerning first=63 second=98 amount=-3 105 | kerning first=63 second=37 amount=-5 106 | kerning first=63 second=38 amount=-5 107 | kerning first=63 second=90 amount=-5 108 | kerning first=63 second=78 amount=-5 109 | kerning first=63 second=80 amount=-5 110 | kerning first=63 second=84 amount=-2 111 | kerning first=63 second=76 amount=-5 112 | kerning first=63 second=58 amount=-1 113 | kerning first=63 second=94 amount=-5 114 | kerning first=63 second=44 amount=-4 115 | kerning first=63 second=96 amount=-2 116 | kerning first=63 second=32 amount=-1 117 | kerning first=65 second=93 amount=-2 118 | kerning first=65 second=100 amount=-2 119 | kerning first=65 second=37 amount=-2 120 | kerning first=65 second=38 amount=-2 121 | kerning first=65 second=90 amount=-3 122 | kerning first=65 second=80 amount=-3 123 | kerning first=65 second=84 amount=-1 124 | kerning first=65 second=76 amount=-4 125 | kerning first=65 second=44 amount=-4 126 | kerning first=65 second=96 amount=-2 127 | kerning first=55 second=100 amount=-2 128 | kerning first=55 second=63 amount=-4 129 | kerning first=55 second=65 amount=-4 130 | kerning first=55 second=68 amount=-4 131 | kerning first=55 second=66 amount=-4 132 | kerning first=55 second=32 amount=-2 133 | kerning first=68 second=92 amount=-4 134 | kerning first=68 second=91 amount=-4 135 | kerning first=68 second=37 amount=-3 136 | kerning first=68 second=38 amount=-3 137 | kerning first=68 second=90 amount=-4 138 | kerning first=68 second=80 amount=-4 139 | kerning first=68 second=84 amount=-2 140 | kerning first=68 second=76 amount=-4 141 | kerning first=68 second=97 amount=-3 142 | kerning first=68 second=44 amount=-4 143 | kerning first=68 second=96 amount=-3 144 | kerning first=68 second=32 amount=-1 145 | kerning first=66 second=93 amount=-1 146 | kerning first=66 second=37 amount=-1 147 | kerning first=66 second=38 amount=-1 148 | kerning first=66 second=90 amount=-1 149 | kerning first=66 second=80 amount=-1 150 | kerning first=66 second=76 amount=-2 151 | kerning first=66 second=44 amount=-2 152 | kerning first=66 second=96 amount=-1 153 | kerning first=70 second=125 amount=-5 154 | kerning first=70 second=123 amount=-5 155 | kerning first=49 second=44 amount=-3 156 | kerning first=60 second=125 amount=-5 157 | kerning first=60 second=123 amount=-5 158 | kerning first=60 second=81 amount=-1 159 | kerning first=59 second=41 amount=-3 160 | kerning first=59 second=101 amount=-1 161 | kerning first=59 second=44 amount=-4 162 | kerning first=59 second=32 amount=-1 163 | kerning first=58 second=81 amount=-3 164 | kerning first=58 second=61 amount=-3 165 | kerning first=43 second=35 amount=-1 166 | kerning first=42 second=105 amount=-3 167 | kerning first=44 second=100 amount=-1 168 | kerning first=44 second=98 amount=-1 169 | kerning first=44 second=63 amount=-4 170 | kerning first=44 second=65 amount=-4 171 | kerning first=44 second=68 amount=-4 172 | kerning first=44 second=66 amount=-2 173 | kerning first=44 second=97 amount=-1 174 | kerning first=44 second=32 amount=-3 175 | kerning first=61 second=63 amount=-1 176 | kerning first=61 second=65 amount=-1 177 | kerning first=61 second=68 amount=-1 178 | kerning first=61 second=66 amount=-1 179 | kerning first=32 second=63 amount=-1 180 | kerning first=32 second=71 amount=-3 181 | kerning first=32 second=68 amount=-1 182 | kerning first=32 second=44 amount=-3 183 | -------------------------------------------------------------------------------- /assets/fonts/futura-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/fonts/futura-48.png -------------------------------------------------------------------------------- /assets/images/bird.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": [ 3 | {"filename":"background.png", "frame":{"x":546,"y":1, "w": 720, "h": 1280}, "rotated":true}, 4 | {"filename":"bird1.png", "frame": {"x":289, "y": 950, "w": 86, "h": 60 }, "rotated":false}, 5 | {"filename":"bird2.png", "frame": {"x":377, "y": 950, "w": 86, "h": 60 }, "rotated":false}, 6 | {"filename":"bird3.png", "frame": {"x":465, "y": 950, "w": 86, "h": 60 }, "rotated":false}, 7 | {"filename":"bottom_pipe.png", "frame": {"x": 284, "y": 1, "w": 129, "h":801},"rotated":false}, 8 | {"filename":"game_name.png", "frame":{"x":1828, "y": 1, "w": 466, "h": 140},"rotated":true}, 9 | {"filename":"gameover.png", "frame":{"x":1828, "y": 469, "w": 500, "h": 136},"rotated":true}, 10 | {"filename":"getready.png", "frame":{"x": 289, "y": 804, "w": 508, "h": 144},"rotated":false}, 11 | {"filename":"ground.png", "frame":{"x":1,"y":1, "w": 840, "h": 281},"rotated":true}, 12 | {"filename":"medal_gold.png", "frame":{"x": 1639, "y": 838, "w": 113, "h": 111},"rotated":true}, 13 | {"filename":"medal_silver.png", "frame":{"x": 639, "y":723, "w": 123, "h": 113},"rotated":false}, 14 | {"filename":"result_board.png", "frame":{"x": 799, "y":723, "w": 590, "h": 298},"rotated":false}, 15 | {"filename":"start.png", "frame":{"x":1, "y": 843, "w": 286, "h": 166 },"rotated":false}, 16 | {"filename":"tap.png", "frame":{"x": 1391, "y": 723, "w": 286, "h": 246},"rotated":true}, 17 | {"filename":"top_pipe.png", "frame":{"x": 415, "y": 1, "w": 129, "h": 801}, "rotated":false} 18 | ], 19 | 20 | "meta":{ 21 | "format":"RGBA8888", 22 | "image":"res_bundle.png", 23 | "size":{"w":1969,"h":1022} 24 | } 25 | } -------------------------------------------------------------------------------- /assets/images/bird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/images/bird.png -------------------------------------------------------------------------------- /assets/sound/collision.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/sound/collision.ogg -------------------------------------------------------------------------------- /assets/sound/drop.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/sound/drop.ogg -------------------------------------------------------------------------------- /assets/sound/point.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/sound/point.ogg -------------------------------------------------------------------------------- /assets/sound/rise.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/sound/rise.ogg -------------------------------------------------------------------------------- /assets/sound/swooshing.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/assets/sound/swooshing.ogg -------------------------------------------------------------------------------- /game.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "korok.io/korok/game" 5 | "korok.io/korok/gui" 6 | "korok.io/korok/gfx" 7 | "korok.io/korok/asset" 8 | "korok.io/korok/engi" 9 | "korok.io/korok" 10 | "korok.io/korok/math/f32" 11 | "korok.io/korok/hid/input" 12 | "korok.io/korok/anim/ween" 13 | "korok.io/korok/math/ease" 14 | "korok.io/korok/audio" 15 | ) 16 | 17 | 18 | type StateEnum int 19 | 20 | const ( 21 | Ready StateEnum = iota 22 | Running 23 | Over 24 | ) 25 | 26 | type BirdStateEnum int 27 | 28 | const ( 29 | Flying BirdStateEnum = iota 30 | Dead 31 | ) 32 | 33 | 34 | const ( 35 | Gravity = 600 36 | TapImpulse = 280 37 | ScrollVelocity = 100 38 | 39 | RotTrigger = 200 40 | MaxAngle = 3.14/6 41 | MinAngle = -3.14/2 42 | AngleVelocity = 3.14 * 4 43 | ) 44 | 45 | type GameScene struct { 46 | state StateEnum 47 | 48 | ready struct{ 49 | gfx.Tex2D 50 | gui.Rect 51 | } 52 | tap struct{ 53 | gfx.Tex2D 54 | gui.Rect 55 | } 56 | gameover struct{ 57 | gfx.Tex2D 58 | gui.Rect 59 | } 60 | score struct{ 61 | gfx.Tex2D 62 | gui.Rect 63 | } 64 | restart struct{ 65 | gfx.Tex2D 66 | gui.Rect 67 | } 68 | 69 | bg engi.Entity 70 | 71 | bird struct{ 72 | state BirdStateEnum 73 | engi.Entity 74 | f32.Vec2 75 | vy float32 76 | w, h float32 77 | rotate float32 78 | } 79 | 80 | ground struct{ 81 | engi.Entity 82 | f32.Vec2 83 | vx float32 84 | } 85 | 86 | sound struct{ 87 | rise uint16 88 | drop uint16 89 | collision uint16 90 | point uint16 91 | swooshing uint16 92 | } 93 | 94 | PipeSystem 95 | alphaTween ween.ColorTween 96 | bounceTween ween.F32Tween 97 | } 98 | 99 | func (sn *GameScene) borrow(bird, bg, ground engi.Entity) { 100 | sn.bird.Entity, sn.bg, sn.ground.Entity = bird, bg, ground 101 | } 102 | 103 | func (sn *GameScene) Load() { 104 | asset.Audio.Load("sound/rise.ogg", false) 105 | asset.Audio.Load("sound/drop.ogg", false) 106 | asset.Audio.Load("sound/point.ogg", false) 107 | asset.Audio.Load("sound/collision.ogg", false) 108 | asset.Audio.Load("sound/swooshing.ogg", false) 109 | } 110 | 111 | func (sn *GameScene) OnEnter(g *game.Game) { 112 | at, _ := asset.Texture.Atlas("images/bird.png") 113 | 114 | // ready and tap image 115 | sn.ready.Tex2D, _ = at.GetByName("getready.png") 116 | sn.ready.Rect = gui.Rect{ 117 | X: (320-233)/2, 118 | Y: 70, 119 | W: 233, 120 | H: 70, 121 | } 122 | sn.tap.Tex2D, _ = at.GetByName("tap.png") 123 | sn.tap.Rect = gui.Rect{ 124 | X: (320-143)/2, 125 | Y: 200, 126 | W: 143, // 286 127 | H: 123, // 246 128 | } 129 | 130 | sn.gameover.Tex2D, _ = at.GetByName("gameover.png") 131 | sn.gameover.Rect = gui.Rect{ 132 | X: (320-233)/2, 133 | Y: 70, 134 | W: 233, 135 | H: 70, 136 | } 137 | sn.score.Tex2D, _ = at.GetByName("result_board.png") 138 | sn.score.Rect = gui.Rect{ 139 | X: (320 - 240)/2, 140 | Y: 200, 141 | W: 240, 142 | H: 120, 143 | } 144 | sn.restart.Tex2D, _ = at.GetByName("start.png") 145 | sn.restart.Rect = gui.Rect{ 146 | X: (320 - 120)/2, 147 | Y: 360, 148 | W: 120, 149 | H: 60, 150 | } 151 | 152 | // sound 153 | sn.sound.rise, _ = asset.Audio.Get("sound/rise.ogg") 154 | sn.sound.drop, _ = asset.Audio.Get("sound/drop.ogg") 155 | sn.sound.point, _ = asset.Audio.Get("sound/point.ogg") 156 | sn.sound.collision, _ = asset.Audio.Get("sound/collision.ogg") 157 | sn.sound.swooshing, _ = asset.Audio.Get("sound/swooshing.ogg") 158 | 159 | 160 | 161 | korok.Transform.Comp(sn.bird.Entity).SetPosition(f32.Vec2{80, 240}) 162 | sn.bird.Vec2 = f32.Vec2{80, 240} 163 | 164 | sn.ground.Vec2 = f32.Vec2{0, 100} 165 | sn.ground.vx = ScrollVelocity 166 | 167 | // setup pipes (129, 801) 168 | top, _ := at.GetByName("top_pipe.png") 169 | bottom, _ := at.GetByName("bottom_pipe.png") 170 | 171 | ps := &sn.PipeSystem 172 | ps.initialize(top, bottom, 6) 173 | ps.setDelay(0) // 3 seconds 174 | ps.setRate(2.5) // generate pipe every 2 seconds 175 | ps.setGap(100) 176 | ps.setLimit(300, 150) 177 | ps.StartScroll() 178 | 179 | sn.alphaTween.Range(gfx.White, gfx.Transparent).Animate(g.TweenEngine.NewAnimator()) 180 | sn.alphaTween.Animator().SetFunction(ease.InOutSine).SetDuration(.5).Forward() 181 | 182 | sn.bounceTween.Range(240, 0).Animate(g.TweenEngine.NewAnimator()) 183 | sn.bounceTween.Animator().SetFunction(ease.OutBounce).SetDuration(1) 184 | } 185 | 186 | func (sn *GameScene) Update(dt float32) { 187 | if sn.alphaTween.Value().A > 0 { 188 | z := gui.SetZOrder(gui.DefaultZOrder+1) 189 | gui.ColorRect(gui.Rect{0,0, 320, 480}, sn.alphaTween.Value(), 0) 190 | gui.SetZOrder(z) 191 | } 192 | 193 | if st := sn.state; st == Ready { 194 | sn.showReady(dt); return 195 | } else if st == Over { 196 | sn.showOver(dt) 197 | return 198 | } 199 | 200 | if input.PointerButton(0).JustPressed() { 201 | sn.bird.vy = TapImpulse 202 | // play effect 203 | audio.PlayEffect(sn.sound.rise, 1) 204 | } 205 | sn.bird.vy -= Gravity * dt 206 | sn.bird.Vec2[1] += sn.bird.vy * dt 207 | 208 | // rotate 209 | if sn.bird.vy > -RotTrigger && sn.bird.rotate < MaxAngle { 210 | sn.bird.rotate += AngleVelocity * dt 211 | } else if sn.bird.vy < -RotTrigger && sn.bird.rotate > MinAngle { 212 | sn.bird.rotate += -AngleVelocity * dt 213 | } 214 | 215 | // update bird position 216 | b := korok.Transform.Comp(sn.bird.Entity) 217 | b.SetPosition(sn.bird.Vec2) 218 | b.SetRotation(sn.bird.rotate) 219 | 220 | // scroll background 221 | // windows.width = 320, ground.width = 420, so if we move by 100(420-320), 222 | // then reset position, it looks like the ground scroll seamless. 223 | x := sn.ground.Vec2[0] 224 | if x < -100 { 225 | x = x + 90 // magic number (bridge start and end of the image) 226 | } 227 | x -= sn.ground.vx * dt 228 | sn.ground.Vec2[0] = x 229 | 230 | // update ground shift 231 | g := korok.Transform.Comp(sn.ground.Entity) 232 | g.SetPosition(sn.ground.Vec2) 233 | 234 | // update pipes 235 | sn.PipeSystem.Update(dt) 236 | // detect collision with pipes 237 | ps := &sn.PipeSystem 238 | if c, _ := ps.CheckCollision(sn.bird.Vec2, f32.Vec2{sn.bird.w, sn.bird.h}); c { 239 | if sn.bird.state != Dead { 240 | ps.StopScroll() 241 | sn.bird.state = Dead 242 | 243 | // stop bird animation 244 | audio.PlayEffect(sn.sound.collision, 1) 245 | korok.Flipbook.Comp(sn.bird.Entity).Stop() 246 | } 247 | } 248 | 249 | 250 | // detect collision with ground and sky 251 | if y := sn.bird.Vec2[1]; y > 480 { 252 | sn.bird.Vec2[1] = 480 253 | } else if y < 100 { 254 | y = 100; sn.state = Over 255 | 256 | if sn.bird.state != Dead { 257 | sn.bird.state = Dead 258 | audio.PlayEffect(sn.sound.collision, 1) 259 | korok.Flipbook.Comp(sn.bird.Entity).Stop() 260 | } 261 | 262 | audio.PlayEffect(sn.sound.drop, 2) 263 | sn.bounceTween.Animator().Forward() // 如何 264 | } 265 | } 266 | 267 | func (sn *GameScene) showReady(dt float32) { 268 | // show ready 269 | gui.Image(1, sn.ready.Rect, sn.ready.Tex2D, nil) 270 | 271 | // show tap hint 272 | gui.Image(2, sn.tap.Rect, sn.tap.Tex2D, nil) 273 | 274 | // check any click 275 | if input.PointerButton(0).JustPressed() { 276 | sn.state = Running 277 | } 278 | } 279 | 280 | func (sn *GameScene) showOver(dt float32) { 281 | // 282 | sn.score.Rect.Y = sn.bounceTween.Value() + 160 283 | sn.restart.Rect.Y = sn.bounceTween.Value() + 320 284 | 285 | 286 | // show game over 287 | gui.Image(1, sn.gameover.Rect, sn.gameover.Tex2D, nil) 288 | 289 | // show score 290 | gui.Image(2, sn.score.Rect, sn.score.Tex2D, nil) 291 | 292 | // show restart button 293 | e := gui.ImageButton(3, sn.restart.Rect, sn.restart.Tex2D, sn.restart.Tex2D, nil) 294 | if e.JustPressed() { 295 | sn.alphaTween.Range(gfx.Transparent, gfx.White) 296 | sn.alphaTween.Animator().SetFunction(ease.InOutSine).SetDuration(.5).OnComplete(func (r bool) { 297 | sn.reStart() 298 | }).Forward() 299 | } 300 | } 301 | 302 | func (sn *GameScene) reStart() { 303 | sn.state = Ready 304 | 305 | // bird 306 | sn.bird.state = Flying 307 | sn.bird.Vec2 = f32.Vec2{80, 240} 308 | sn.bird.vy = 0 309 | sn.bird.rotate = 0 310 | korok.Transform.Comp(sn.bird.Entity).SetRotation(0) 311 | korok.Flipbook.Comp(sn.bird.Entity).Play("flying") 312 | // pipes 313 | sn.PipeSystem.Reset() 314 | sn.PipeSystem.StartScroll() 315 | 316 | // reverse 317 | sn.alphaTween.Animator().OnComplete(nil).Reverse() 318 | } 319 | 320 | 321 | func (sn *GameScene) OnExit() { 322 | } 323 | -------------------------------------------------------------------------------- /images/flappybird.final.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntop001/flappybird/e6656e99523de3c150f737779394588c376e79b8/images/flappybird.final.gif -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "korok.io/korok/game" 5 | "korok.io/korok" 6 | "korok.io/korok/asset" 7 | "korok.io/korok/math/f32" 8 | "korok.io/korok/gfx" 9 | "korok.io/korok/gui" 10 | "korok.io/korok/engi" 11 | "korok.io/korok/anim" 12 | "korok.io/korok/math/ease" 13 | "korok.io/korok/gfx/font" 14 | ) 15 | 16 | type StartScene struct { 17 | title struct{ 18 | gfx.Tex2D 19 | gui.Rect 20 | } 21 | start struct{ 22 | btnNormal gfx.Tex2D 23 | btnPressed gfx.Tex2D 24 | gui.Rect 25 | } 26 | bird, bg, ground engi.Entity 27 | mask gfx.Color 28 | } 29 | 30 | func (sn *StartScene) Load() { 31 | asset.Texture.LoadAtlas("images/bird.png", "images/bird.json") 32 | asset.Font.LoadTrueType("font1", "fonts/Marker Felt.ttf", font.ASCII(32)) 33 | } 34 | 35 | func (sn *StartScene) OnEnter(g *game.Game) { 36 | font, _ := asset.Font.Get("font1") 37 | gui.SetFont(font) 38 | 39 | at, _ := asset.Texture.Atlas("images/bird.png") 40 | bg, _ := at.GetByName("background.png") 41 | ground, _ := at.GetByName("ground.png") 42 | 43 | // setup gui 44 | // title 45 | tt, _ := at.GetByName("game_name.png") 46 | sn.title.Tex2D = tt 47 | sn.title.Rect = gui.Rect { 48 | X: (320 - 233)/2, 49 | Y: 80, 50 | W: 233, 51 | H: 70, 52 | } 53 | 54 | // start button 55 | btn, _ := at.GetByName("start.png") 56 | sn.start.btnNormal = btn 57 | sn.start.btnPressed = btn 58 | sn.start.Rect = gui.Rect{ 59 | X: (320 - 120)/2, 60 | Y: 300, 61 | W: 120, 62 | H: 60, 63 | } 64 | 65 | // setup bg 66 | { 67 | entity := korok.Entity.New() 68 | spr := korok.Sprite.NewCompX(entity, bg) 69 | spr.SetSize(320, 480) 70 | xf := korok.Transform.NewComp(entity) 71 | xf.SetPosition( f32.Vec2{160, 240}) 72 | sn.bg = entity 73 | } 74 | 75 | // setup ground {840 281} 76 | { 77 | entity := korok.Entity.New() 78 | spr := korok.Sprite.NewCompX(entity, ground) 79 | spr.SetSize(420, 140) 80 | spr.SetGravity(0, 1) 81 | spr.SetZOrder(1) 82 | xf := korok.Transform.NewComp(entity) 83 | xf.SetPosition(f32.Vec2{0, 100}) 84 | sn.ground = entity 85 | } 86 | 87 | // flying animation 88 | bird1, _ := at.GetByName("bird1.png") 89 | bird2, _ := at.GetByName("bird2.png") 90 | bird3, _ := at.GetByName("bird3.png") 91 | 92 | frames := []gfx.Tex2D{bird1, bird2, bird3} 93 | g.AnimationSystem.SpriteEngine.NewAnimation("flying", frames, true) 94 | 95 | // setup bird 96 | bird := korok.Entity.New() 97 | spr := korok.Sprite.NewCompX(bird, bird1) 98 | spr.SetSize(48, 32) 99 | spr.SetZOrder(2) 100 | xf := korok.Transform.NewComp(bird) 101 | xf.SetPosition(f32.Vec2{160, 240}) 102 | 103 | anim := korok.Flipbook.NewComp(bird) 104 | anim.SetRate(.1) 105 | anim.Play("flying") 106 | 107 | sn.bird = bird 108 | } 109 | func (sn *StartScene) Update(dt float32) { 110 | // draw title 111 | gui.Image(1, sn.title.Rect, sn.title.Tex2D, nil) 112 | 113 | // draw start button 114 | e := gui.ImageButton(2, sn.start.Rect, sn.start.btnNormal, sn.start.btnPressed, nil) 115 | if e.JustPressed() { 116 | // sn.LoadGame() 117 | sn.fadeOut() 118 | } 119 | // fade color 120 | if sn.mask.A > 0 { 121 | gui.ColorRect(gui.Rect{W:320,H:480}, sn.mask,0) 122 | } 123 | } 124 | func (sn *StartScene) OnExit() { 125 | } 126 | 127 | func (sn *StartScene) fadeOut() { 128 | anim.OfColor(&sn.mask, gfx.Transparent, gfx.White).SetFunction(ease.InOutSine).SetDuration(1).OnComplete(func(reverse bool) { 129 | sn.LoadGame() 130 | }).Forward() 131 | } 132 | 133 | func (sn *StartScene) LoadGame() { 134 | gsn := &GameScene{} 135 | gsn.borrow(sn.bird, sn.bg, sn.ground) 136 | 137 | // load game scene 138 | korok.SceneMan.Load(gsn) 139 | korok.SceneMan.Push(gsn) 140 | } 141 | 142 | func main() { 143 | options := korok.Options{ 144 | Title:"Flappy Bird", 145 | Width:320, 146 | Height:480, 147 | } 148 | korok.Run(&options, &StartScene{}) 149 | } 150 | -------------------------------------------------------------------------------- /pipe.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "korok.io/korok/engi" 5 | "korok.io/korok" 6 | "korok.io/korok/gfx" 7 | "korok.io/korok/math" 8 | "korok.io/korok/math/f32" 9 | ) 10 | 11 | // Pipe manager system. 12 | 13 | type Pipe struct { 14 | top struct{ 15 | engi.Entity 16 | f32.Vec2 17 | } 18 | bottom struct{ 19 | engi.Entity 20 | f32.Vec2 21 | } 22 | high float32 23 | active bool 24 | x, vx float32 25 | } 26 | 27 | func (p *Pipe) initialize(texTop, texBottom gfx.Tex2D) { 28 | top := korok.Entity.New() 29 | spr := korok.Sprite.NewComp(top) 30 | spr.SetSprite(texTop) 31 | spr.SetSize(65, 400) 32 | spr.SetGravity(.5, 0) 33 | 34 | bottom := korok.Entity.New() 35 | spr = korok.Sprite.NewComp(bottom) 36 | spr.SetSize(65, 400) 37 | spr.SetSprite(texBottom) 38 | spr.SetGravity(.5, 1) 39 | 40 | // out of screen 41 | korok.Transform.NewComp(top).SetPosition(f32.Vec2{-100, 210}) 42 | korok.Transform.NewComp(bottom).SetPosition(f32.Vec2{-100, 160}) 43 | 44 | p.top.Entity = top 45 | p.bottom.Entity = bottom 46 | p.vx = ScrollVelocity 47 | } 48 | 49 | func (p *Pipe) reset(x, high, gap float32) { 50 | p.active = true 51 | p.x = x 52 | p.top.Vec2 = f32.Vec2{x, high + gap} 53 | p.bottom.Vec2 = f32.Vec2{x, high} 54 | } 55 | 56 | func (p *Pipe) update(dt float32) { 57 | p.x -= p.vx * dt 58 | if p.x < -50 { 59 | p.active = false 60 | } 61 | 62 | p.top.Vec2[0] = p.x 63 | p.bottom.Vec2[0] = p.x 64 | 65 | korok.Transform.Comp(p.top.Entity).SetPosition(p.top.Vec2) 66 | korok.Transform.Comp(p.bottom.Entity).SetPosition(p.bottom.Vec2) 67 | } 68 | 69 | type PipeSystem struct { 70 | gap, top, bottom float32 // gap, top, bottom limit 71 | respawn float32 // respawn location 72 | scroll bool 73 | 74 | delay struct{ 75 | clock float32 76 | limit float32 77 | } 78 | generate struct{ 79 | clock float32 80 | limit float32 81 | } 82 | 83 | pipes []*Pipe 84 | frees []*Pipe 85 | 86 | _pool []Pipe 87 | } 88 | 89 | func (ps *PipeSystem) initialize(texTop, texBottom gfx.Tex2D, size int) { 90 | ps._pool = make([]Pipe, size) 91 | ps.frees = make([]*Pipe, size) // add to freelist 92 | for i := range ps._pool { 93 | ps.frees[i] = &ps._pool[i] 94 | ps.frees[i].initialize(texTop, texBottom) 95 | } 96 | ps.respawn = 320 + 20 97 | } 98 | 99 | func (ps *PipeSystem) setDelay(d float32) { 100 | ps.delay.limit = d 101 | } 102 | 103 | func (ps *PipeSystem) setRate(r float32) { 104 | ps.generate.limit = r 105 | } 106 | 107 | func (ps *PipeSystem) setGap(gap float32) { 108 | ps.gap = gap 109 | } 110 | 111 | func (ps *PipeSystem) setLimit(top, b float32) { 112 | ps.top, ps.bottom = top, b 113 | } 114 | 115 | func (ps *PipeSystem) Update(dt float32) { 116 | if !ps.scroll { 117 | return 118 | } 119 | 120 | // delay some time 121 | if d := &ps.delay; d.clock < d.limit { 122 | d.clock += dt; return 123 | } 124 | 125 | // generate new pipe 126 | if g := &ps.generate; g.clock < g.limit { 127 | g.clock += dt 128 | } else { 129 | g.clock = 0 130 | ps.newPipe() 131 | } 132 | 133 | // update pipe 134 | for _, p := range ps.pipes { 135 | p.update(dt) 136 | } 137 | 138 | // recycle 139 | ps.recycle() 140 | } 141 | 142 | func (ps *PipeSystem) StopScroll() { 143 | ps.scroll = false 144 | } 145 | 146 | func (ps *PipeSystem) StartScroll() { 147 | ps.scroll = true 148 | } 149 | 150 | func (ps *PipeSystem) Reset() { 151 | for _, p := range ps.pipes { 152 | p.x = -100 153 | // out of screen 154 | korok.Transform.NewComp(p.top.Entity).SetPosition(f32.Vec2{-100, 210}) 155 | korok.Transform.NewComp(p.bottom.Entity).SetPosition(f32.Vec2{-100, 160}) 156 | } 157 | } 158 | 159 | func (ps *PipeSystem) newPipe() { 160 | if sz := len(ps.frees); sz > 0 { 161 | p := ps.frees[sz-1]; ps.frees = ps.frees[:sz-1] 162 | ps.pipes = append(ps.pipes, p) 163 | p.reset(ps.respawn, math.Random(ps.bottom, ps.top), ps.gap) 164 | } 165 | } 166 | 167 | // inactive pipes come first 168 | func (ps *PipeSystem) recycle() { 169 | pipes, inactive := ps.pipes, -1 170 | for i, p := range pipes { 171 | if p.active { 172 | break 173 | } 174 | inactive = i 175 | } 176 | if inactive >= 0 { 177 | ps.pipes = pipes[inactive+1:] 178 | ps.frees = append(ps.frees, pipes[:inactive+1]...) 179 | } 180 | } 181 | 182 | // 左下坐标 183 | type AABB struct { 184 | x, y float32 185 | width, height float32 186 | } 187 | 188 | func OverlapAB(a, b *AABB) bool { 189 | if a.x < b.x+b.width && a.x+a.width>b.x && a.y < b.y+b.height && a.y+a.height > b.y { 190 | return true 191 | } 192 | return false 193 | } 194 | 195 | // check collision 196 | func (ps *PipeSystem) CheckCollision(p f32.Vec2, sz f32.Vec2) (bool, float32) { 197 | tolerance := float32(8) 198 | sz[0], sz[1] = sz[0]-tolerance, sz[1]-tolerance 199 | bird := &AABB{p[0]-sz[0]/2, p[1]-sz[1]/2, sz[0], sz[1]} 200 | for _, p := range ps.pipes { 201 | top := &AABB{ 202 | p.top.Vec2[0] - 32, 203 | p.top.Vec2[1], 204 | 65, 205 | 400, 206 | } 207 | if OverlapAB(bird, top) { 208 | return true, bird.x - top.x 209 | } 210 | 211 | bottom := &AABB{ 212 | p.bottom.Vec2[0] - 32, 213 | p.bottom.Vec2[1] - 400, 214 | 65, 215 | 400, 216 | } 217 | if OverlapAB(bird, bottom) { 218 | return true, bird.x - top.x 219 | } 220 | } 221 | return false, 0 222 | } 223 | 224 | 225 | --------------------------------------------------------------------------------