├── README.md ├── demo.mp4 ├── demo2.mp4 ├── game.xls ├── gfx.py ├── images ├── chrome.png ├── code.png ├── csv1.png ├── csv2.png ├── demo.jpg ├── display.jpg ├── excel.png ├── v2_gameover.jpg ├── v2_pause.jpg └── v2_playing.jpg ├── main.py ├── ssd1306.py └── v2 ├── README.md ├── bg.pbm ├── cacti.pbm ├── gameover.pbm ├── gfx.py ├── main.py ├── player.pbm ├── player1.pbm ├── player2.pbm └── ssd1306.py /README.md: -------------------------------------------------------------------------------- 1 | # chrome offline game on esp8266 2 | 3 | 当前更新到2.0版本 4 | 5 | 请猛戳此处 https://github.com/OpensourceBooks/chrome_offline_game_on_esp8266/blob/master/v2/README.md 6 | 7 | 以下是1.0版本 8 | 9 | 在esp8266上面开发一款chrome浏览器离线时可玩的那款恐龙跳仙人掌的游戏。 10 | 11 | ### 原型 12 | 13 | 游戏的原型是chrome浏览器在离线时呈现的小恐龙跳仙人掌的游戏 14 | 15 | ![](images/chrome.png) 16 | 17 | 视频演示地址:https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/master/demo.mp4 18 | 19 | ------ 20 | 21 | ### 制作过程 22 | 23 | 所需的元器件: 24 | 25 | 1. NodeMCU(esp8266芯片、4M内存) 26 | 2. 12864 oled (ssd1306 驱动) 27 | 3. 大按键按钮 (低电平触发) 28 | 4. 杜邦线若干(母对母) 29 | 30 | 组装的效果图: 31 | 32 | ![](images/demo.jpg) 33 | 34 | ### 使用excel表格,制作游戏的像素图案 35 | 36 | 1. 使用excel表格,绘制恐龙和仙人掌 37 | 38 | ![](images/excel.png) 39 | 40 | 2. 用1和0进行填充,保存为csv文件,并用文本编辑器打开 41 | 42 | ![](images/csv1.png) 43 | 44 | ![](images/csv2.png) 45 | 46 | 3. 写到代码里 47 | 48 | ``` 49 | player = {} 50 | player["x"] = 10 51 | player["y"] = 44 52 | player["pixel"] = [ 53 | (0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0), 54 | (0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1), 55 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1), 56 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1), 57 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1), 58 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0), 59 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0), 60 | (0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0), 61 | (1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0), 62 | (1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0), 63 | (1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,1,0,0,0,0), 64 | (1,1,1,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0), 65 | (1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0), 66 | (0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0), 67 | (0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0), 68 | (0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0), 69 | (0,0,0,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0), 70 | (0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0), 71 | (0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0), 72 | (0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0) 73 | ] 74 | ``` 75 | 76 | 77 | 78 | ![](images/code.png) 79 | 80 | 4. 循环像素,显示小恐龙: 81 | 82 | ``` 83 | pixels = player["pixel"] 84 | 85 | if(status["is_jump"]): 86 | player["y"]-=3 87 | if(player["y"]<15): 88 | status["is_jump"]=False 89 | else: 90 | player["y"]+=3 91 | if(player["y"]>=43): 92 | player["y"]=43 93 | status["is_jumpfinish"]=True 94 | 95 | for i in range(0,len(pixels)): 96 | for ii in range(0,len(pixels[i])): 97 | oled.pixel(player["x"]+ii,player["y"]+i,pixels[i][ii]) 98 | ``` 99 | 100 | ![](images/display.jpg) 101 | 102 | 103 | 这里提供一个绘制好的excel文件 game.xls 104 | 105 | 最后,代码全部在本仓库里。 106 | 107 | 108 | 109 | 欢迎喜欢玩esp8266的一起来切磋,微信号:cr4fun 110 | 111 | 视频演示地址:https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/master/demo.mp4 -------------------------------------------------------------------------------- /demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/demo.mp4 -------------------------------------------------------------------------------- /demo2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/demo2.mp4 -------------------------------------------------------------------------------- /game.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/game.xls -------------------------------------------------------------------------------- /gfx.py: -------------------------------------------------------------------------------- 1 | # Port of Adafruit GFX Arduino library to MicroPython. 2 | # Based on: https://github.com/adafruit/Adafruit-GFX-Library 3 | # Author: Tony DiCola (original GFX author Phil Burgess) 4 | # License: MIT License (https://opensource.org/licenses/MIT) 5 | 6 | 7 | class GFX: 8 | 9 | def __init__(self, width, height, pixel, hline=None, vline=None): 10 | # Create an instance of the GFX drawing class. You must pass in the 11 | # following parameters: 12 | # - width = The width of the drawing area in pixels. 13 | # - height = The height of the drawing area in pixels. 14 | # - pixel = A function to call when a pixel is drawn on the display. 15 | # This function should take at least an x and y position 16 | # and then any number of optional color or other parameters. 17 | # You can also provide the following optional keyword argument to 18 | # improve the performance of drawing: 19 | # - hline = A function to quickly draw a horizontal line on the display. 20 | # This should take at least an x, y, and width parameter and 21 | # any number of optional color or other parameters. 22 | # - vline = A function to quickly draw a vertical line on the display. 23 | # This should take at least an x, y, and height paraemter and 24 | # any number of optional color or other parameters. 25 | self.width = width 26 | self.height = height 27 | self._pixel = pixel 28 | # Default to slow horizontal & vertical line implementations if no 29 | # faster versions are provided. 30 | if hline is None: 31 | self.hline = self._slow_hline 32 | else: 33 | self.hline = hline 34 | if vline is None: 35 | self.vline = self._slow_vline 36 | else: 37 | self.vline = vline 38 | 39 | def _slow_hline(self, x0, y0, width, *args, **kwargs): 40 | # Slow implementation of a horizontal line using pixel drawing. 41 | # This is used as the default horizontal line if no faster override 42 | # is provided. 43 | if y0 < 0 or y0 > self.height or x0 < -width or x0 > self.width: 44 | return 45 | for i in range(width): 46 | self._pixel(x0+i, y0, *args, **kwargs) 47 | 48 | def _slow_vline(self, x0, y0, height, *args, **kwargs): 49 | # Slow implementation of a vertical line using pixel drawing. 50 | # This is used as the default vertical line if no faster override 51 | # is provided. 52 | if y0 < -height or y0 > self.height or x0 < 0 or x0 > self.width: 53 | return 54 | for i in range(height): 55 | self._pixel(x0, y0+i, *args, **kwargs) 56 | 57 | def rect(self, x0, y0, width, height, *args, **kwargs): 58 | # Rectangle drawing function. Will draw a single pixel wide rectangle 59 | # starting in the upper left x0, y0 position and width, height pixels in 60 | # size. 61 | if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width: 62 | return 63 | self.hline(x0, y0, width, *args, **kwargs) 64 | self.hline(x0, y0+height-1, width, *args, **kwargs) 65 | self.vline(x0, y0, height, *args, **kwargs) 66 | self.vline(x0+width-1, y0, height, *args, **kwargs) 67 | 68 | def fill_rect(self, x0, y0, width, height, *args, **kwargs): 69 | # Filled rectangle drawing function. Will draw a single pixel wide 70 | # rectangle starting in the upper left x0, y0 position and width, height 71 | # pixels in size. 72 | if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width: 73 | return 74 | for i in range(x0, x0+width): 75 | self.vline(i, y0, height, *args, **kwargs) 76 | 77 | def line(self, x0, y0, x1, y1, *args, **kwargs): 78 | # Line drawing function. Will draw a single pixel wide line starting at 79 | # x0, y0 and ending at x1, y1. 80 | steep = abs(y1 - y0) > abs(x1 - x0) 81 | if steep: 82 | x0, y0 = y0, x0 83 | x1, y1 = y1, x1 84 | if x0 > x1: 85 | x0, x1 = x1, x0 86 | y0, y1 = y1, y0 87 | dx = x1 - x0 88 | dy = abs(y1 - y0) 89 | err = dx // 2 90 | ystep = 0 91 | if y0 < y1: 92 | ystep = 1 93 | else: 94 | ystep = -1 95 | while x0 <= x1: 96 | if steep: 97 | self._pixel(y0, x0, *args, **kwargs) 98 | else: 99 | self._pixel(x0, y0, *args, **kwargs) 100 | err -= dy 101 | if err < 0: 102 | y0 += ystep 103 | err += dx 104 | x0 += 1 105 | 106 | def circle(self, x0, y0, radius, *args, **kwargs): 107 | # Circle drawing function. Will draw a single pixel wide circle with 108 | # center at x0, y0 and the specified radius. 109 | f = 1 - radius 110 | ddF_x = 1 111 | ddF_y = -2 * radius 112 | x = 0 113 | y = radius 114 | self._pixel(x0, y0 + radius, *args, **kwargs) 115 | self._pixel(x0, y0 - radius, *args, **kwargs) 116 | self._pixel(x0 + radius, y0, *args, **kwargs) 117 | self._pixel(x0 - radius, y0, *args, **kwargs) 118 | while x < y: 119 | if f >= 0: 120 | y -= 1 121 | ddF_y += 2 122 | f += ddF_y 123 | x += 1 124 | ddF_x += 2 125 | f += ddF_x 126 | self._pixel(x0 + x, y0 + y, *args, **kwargs) 127 | self._pixel(x0 - x, y0 + y, *args, **kwargs) 128 | self._pixel(x0 + x, y0 - y, *args, **kwargs) 129 | self._pixel(x0 - x, y0 - y, *args, **kwargs) 130 | self._pixel(x0 + y, y0 + x, *args, **kwargs) 131 | self._pixel(x0 - y, y0 + x, *args, **kwargs) 132 | self._pixel(x0 + y, y0 - x, *args, **kwargs) 133 | self._pixel(x0 - y, y0 - x, *args, **kwargs) 134 | 135 | def fill_circle(self, x0, y0, radius, *args, **kwargs): 136 | # Filled circle drawing function. Will draw a filled circule with 137 | # center at x0, y0 and the specified radius. 138 | self.vline(x0, y0 - radius, 2*radius + 1, *args, **kwargs) 139 | f = 1 - radius 140 | ddF_x = 1 141 | ddF_y = -2 * radius 142 | x = 0 143 | y = radius 144 | while x < y: 145 | if f >= 0: 146 | y -= 1 147 | ddF_y += 2 148 | f += ddF_y 149 | x += 1 150 | ddF_x += 2 151 | f += ddF_x 152 | self.vline(x0 + x, y0 - y, 2*y + 1, *args, **kwargs) 153 | self.vline(x0 + y, y0 - x, 2*x + 1, *args, **kwargs) 154 | self.vline(x0 - x, y0 - y, 2*y + 1, *args, **kwargs) 155 | self.vline(x0 - y, y0 - x, 2*x + 1, *args, **kwargs) 156 | 157 | def triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs): 158 | # Triangle drawing function. Will draw a single pixel wide triangle 159 | # around the points (x0, y0), (x1, y1), and (x2, y2). 160 | self.line(x0, y0, x1, y1, *args, **kwargs) 161 | self.line(x1, y1, x2, y2, *args, **kwargs) 162 | self.line(x2, y2, x0, y0, *args, **kwargs) 163 | 164 | def fill_triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs): 165 | # Filled triangle drawing function. Will draw a filled triangle around 166 | # the points (x0, y0), (x1, y1), and (x2, y2). 167 | if y0 > y1: 168 | y0, y1 = y1, y0 169 | x0, x1 = x1, x0 170 | if y1 > y2: 171 | y2, y1 = y1, y2 172 | x2, x1 = x1, x2 173 | if y0 > y1: 174 | y0, y1 = y1, y0 175 | x0, x1 = x1, x0 176 | a = 0 177 | b = 0 178 | y = 0 179 | last = 0 180 | if y0 == y2: 181 | a = x0 182 | b = x0 183 | if x1 < a: 184 | a = x1 185 | elif x1 > b: 186 | b = x1 187 | if x2 < a: 188 | a = x2 189 | elif x2 > b: 190 | b = x2 191 | self.hline(a, y0, b-a+1, *args, **kwargs) 192 | return 193 | dx01 = x1 - x0 194 | dy01 = y1 - y0 195 | dx02 = x2 - x0 196 | dy02 = y2 - y0 197 | dx12 = x2 - x1 198 | dy12 = y2 - y1 199 | if dy01 == 0: 200 | dy01 = 1 201 | if dy02 == 0: 202 | dy02 = 1 203 | if dy12 == 0: 204 | dy12 = 1 205 | sa = 0 206 | sb = 0 207 | if y1 == y2: 208 | last = y1 209 | else: 210 | last = y1-1 211 | for y in range(y0, last+1): 212 | a = x0 + sa // dy01 213 | b = x0 + sb // dy02 214 | sa += dx01 215 | sb += dx02 216 | if a > b: 217 | a, b = b, a 218 | self.hline(a, y, b-a+1, *args, **kwargs) 219 | sa = dx12 * (y - y1) 220 | sb = dx02 * (y - y0) 221 | while y <= y2: 222 | a = x1 + sa // dy12 223 | b = x0 + sb // dy02 224 | sa += dx12 225 | sb += dx02 226 | if a > b: 227 | a, b = b, a 228 | self.hline(a, y, b-a+1, *args, **kwargs) 229 | y += 1 230 | -------------------------------------------------------------------------------- /images/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/chrome.png -------------------------------------------------------------------------------- /images/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/code.png -------------------------------------------------------------------------------- /images/csv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/csv1.png -------------------------------------------------------------------------------- /images/csv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/csv2.png -------------------------------------------------------------------------------- /images/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/demo.jpg -------------------------------------------------------------------------------- /images/display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/display.jpg -------------------------------------------------------------------------------- /images/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/excel.png -------------------------------------------------------------------------------- /images/v2_gameover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/v2_gameover.jpg -------------------------------------------------------------------------------- /images/v2_pause.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/v2_pause.jpg -------------------------------------------------------------------------------- /images/v2_playing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/images/v2_playing.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from machine import Pin,I2C,SPI 2 | import ssd1306 3 | import gfx 4 | from time import sleep 5 | 6 | pin_blue = Pin(4, Pin.IN) 7 | pin_red = Pin(5, Pin.IN) 8 | 9 | i2c = I2C(scl=Pin(2), sda=Pin(0), freq=100000) 10 | oled = ssd1306.SSD1306_I2C(128,64, i2c) 11 | 12 | #oled = ssd1306.SSD1306_I2C(128, 32, i2c, addr=0x27) 13 | graphics = gfx.GFX(128, 64, oled.pixel) 14 | oled.poweron() 15 | oled.init_display() 16 | oled.fill(0) 17 | 18 | oled.show() 19 | 20 | def blue_click(): 21 | if(status["is_jumpfinish"]): 22 | status["is_jump"]=True 23 | status["is_jumpfinish"]=False 24 | 25 | 26 | def red_click(): 27 | #start game 28 | if(status["game"]=="ready"): 29 | status["game"]="playing" 30 | #fire 31 | if(status["game"]=="playing"): 32 | fire() 33 | #restart game 34 | if(status["game"]=="gameover"): 35 | begin() 36 | status["game"]="playing" 37 | 38 | def fire(): 39 | pass 40 | 41 | status={} 42 | 43 | status["game"]="loading" 44 | 45 | status["gametime"]=0 46 | status["km"]=0 47 | 48 | status["is_jump"]=False 49 | status["is_fire"]=False 50 | status["is_jumpfinish"]=True 51 | 52 | 53 | 54 | 55 | 56 | 57 | player = {} 58 | player["x"] = 10 59 | player["y"] = 44 60 | player["pixel"] = [ 61 | (0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0), 62 | (0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1), 63 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1), 64 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1), 65 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1), 66 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0), 67 | (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0), 68 | (0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0), 69 | (1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0), 70 | (1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0), 71 | (1,1,0,0,0,0,1,1,1,1,1,1,1,1,0,1,0,0,0,0), 72 | (1,1,1,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0), 73 | (1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0), 74 | (0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0), 75 | (0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0), 76 | (0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0), 77 | (0,0,0,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0), 78 | (0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0), 79 | (0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0), 80 | (0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0) 81 | ] 82 | 83 | obj={} 84 | obj["x"] = 130 85 | obj["y"] = 44 86 | obj["pixel"] = [ 87 | (0,0,0,0,1,1,1,1,0,0,0,0), 88 | (0,0,0,1,1,1,1,1,1,0,0,0), 89 | (0,0,0,1,1,1,1,1,1,0,0,0), 90 | (0,0,0,1,1,1,1,1,1,0,0,0), 91 | (0,0,0,1,1,1,1,1,1,0,0,0), 92 | (0,0,0,1,1,1,1,1,1,0,0,0), 93 | (1,1,0,1,1,1,1,1,1,0,1,1), 94 | (1,1,0,1,1,1,1,1,1,0,1,1), 95 | (1,1,0,1,1,1,1,1,1,0,1,1), 96 | (1,1,0,1,1,1,1,1,1,0,1,1), 97 | (1,1,0,1,1,1,1,1,1,0,1,1), 98 | (1,1,0,1,1,1,1,1,1,0,1,1), 99 | (0,1,1,1,1,1,1,1,1,1,1,0), 100 | (0,0,1,1,1,1,1,1,1,1,0,0), 101 | (0,0,0,1,1,1,1,1,1,0,0,0), 102 | (0,0,0,1,1,1,1,1,1,0,0,0), 103 | (0,0,0,1,1,1,1,1,1,0,0,0), 104 | (0,0,0,1,1,1,1,1,1,0,0,0), 105 | (0,0,0,1,1,1,1,1,1,0,0,0), 106 | (0,0,0,1,1,1,1,1,1,0,0,0) 107 | ] 108 | 109 | 110 | def begin(): 111 | status["game"]="loading" 112 | status["gametime"]=0 113 | status["km"]=0 114 | status["is_jump"]=False 115 | status["is_fire"]=False 116 | status["is_jumpfinish"]=True 117 | 118 | obj["x"] = 130 119 | obj["y"] = 44 120 | player["y"] = 44 121 | 122 | 123 | def draw_player(): 124 | 125 | pixels = player["pixel"] 126 | 127 | if(status["is_jump"]): 128 | player["y"]-=3 129 | if(player["y"]<15): 130 | status["is_jump"]=False 131 | else: 132 | player["y"]+=3 133 | if(player["y"]>=43): 134 | player["y"]=43 135 | status["is_jumpfinish"]=True 136 | 137 | for i in range(0,len(pixels)): 138 | for ii in range(0,len(pixels[i])): 139 | oled.pixel(player["x"]+ii,player["y"]+i,pixels[i][ii]) 140 | 141 | def draw_obj(): 142 | obj["x"]-=4 143 | 144 | pixels = obj["pixel"] 145 | for i in range(0,len(pixels)): 146 | for ii in range(0,len(pixels[i])): 147 | oled.pixel(obj["x"]+ii,obj["y"]+i,pixels[i][ii]) 148 | 149 | if(obj["x"]<=-10): 150 | obj["x"]=130 151 | 152 | def check(): 153 | if(obj["x"]-player["x"]<15 and obj["y"]-player["y"]<15): 154 | status["game"]="gameover" 155 | pass 156 | # pixels_a = player["pixel"] 157 | # pixels_b = obj["pixel"] 158 | 159 | 160 | while (True): 161 | oled.fill(0) 162 | oled.contrast(1) 163 | 164 | blue = pin_blue.value() 165 | red = pin_red.value() 166 | 167 | if(pin_red.value() == 0): 168 | red_click() 169 | if(status["game"]=="loading"): 170 | oled.text("loading game.".format(status["km"]),10,30) 171 | oled.show() 172 | sleep(1) 173 | oled.text("loading game..".format(status["km"]),10,30) 174 | oled.show() 175 | sleep(1) 176 | oled.text("loading game...".format(status["km"]),10,30) 177 | oled.show() 178 | sleep(1) 179 | status["game"]="ready" 180 | if(status["game"]=="ready"): 181 | oled.text("> play".format(status["km"]),10,20) 182 | oled.text("code by".format(status["km"]),10,30) 183 | oled.text("cr4fun".format(status["km"]),10,40) 184 | if(status["game"]=="playing"): 185 | if(pin_blue.value() == 0): 186 | blue_click() 187 | status["km"]+=1 188 | status["gametime"]+=1 189 | graphics.line(0, 63, 127, 63, 1) 190 | oled.text("{0} km".format(status["km"]),2,0) 191 | draw_player() 192 | draw_obj() 193 | check() 194 | if(status["game"]=="gameover"): 195 | oled.text("{0} km".format(status["km"]),2,0) 196 | oled.text("game over",25,30) 197 | oled.show() 198 | -------------------------------------------------------------------------------- /ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | 3 | import time 4 | import framebuf 5 | 6 | 7 | # register definitions 8 | SET_CONTRAST = const(0x81) 9 | SET_ENTIRE_ON = const(0xa4) 10 | SET_NORM_INV = const(0xa6) 11 | SET_DISP = const(0xae) 12 | SET_MEM_ADDR = const(0x20) 13 | SET_COL_ADDR = const(0x21) 14 | SET_PAGE_ADDR = const(0x22) 15 | SET_DISP_START_LINE = const(0x40) 16 | SET_SEG_REMAP = const(0xa0) 17 | SET_MUX_RATIO = const(0xa8) 18 | SET_COM_OUT_DIR = const(0xc0) 19 | SET_DISP_OFFSET = const(0xd3) 20 | SET_COM_PIN_CFG = const(0xda) 21 | SET_DISP_CLK_DIV = const(0xd5) 22 | SET_PRECHARGE = const(0xd9) 23 | SET_VCOM_DESEL = const(0xdb) 24 | SET_CHARGE_PUMP = const(0x8d) 25 | 26 | 27 | class SSD1306: 28 | def __init__(self, width, height, external_vcc): 29 | self.width = width 30 | self.height = height 31 | self.external_vcc = external_vcc 32 | self.pages = self.height // 8 33 | # Note the subclass must initialize self.framebuf to a framebuffer. 34 | # This is necessary because the underlying data buffer is different 35 | # between I2C and SPI implementations (I2C needs an extra byte). 36 | self.poweron() 37 | self.init_display() 38 | 39 | def init_display(self): 40 | for cmd in ( 41 | SET_DISP | 0x00, # off 42 | # address setting 43 | SET_MEM_ADDR, 0x00, # horizontal 44 | # resolution and layout 45 | SET_DISP_START_LINE | 0x00, 46 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 47 | SET_MUX_RATIO, self.height - 1, 48 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 49 | SET_DISP_OFFSET, 0x00, 50 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 51 | # timing and driving scheme 52 | SET_DISP_CLK_DIV, 0x80, 53 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 54 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 55 | # display 56 | SET_CONTRAST, 0xff, # maximum 57 | SET_ENTIRE_ON, # output follows RAM contents 58 | SET_NORM_INV, # not inverted 59 | # charge pump 60 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 61 | SET_DISP | 0x01): # on 62 | self.write_cmd(cmd) 63 | self.fill(0) 64 | self.show() 65 | 66 | def poweroff(self): 67 | self.write_cmd(SET_DISP | 0x00) 68 | 69 | def contrast(self, contrast): 70 | self.write_cmd(SET_CONTRAST) 71 | self.write_cmd(contrast) 72 | 73 | def invert(self, invert): 74 | self.write_cmd(SET_NORM_INV | (invert & 1)) 75 | 76 | def show(self): 77 | x0 = 0 78 | x1 = self.width - 1 79 | if self.width == 64: 80 | # displays with width of 64 pixels are shifted by 32 81 | x0 += 32 82 | x1 += 32 83 | self.write_cmd(SET_COL_ADDR) 84 | self.write_cmd(x0) 85 | self.write_cmd(x1) 86 | self.write_cmd(SET_PAGE_ADDR) 87 | self.write_cmd(0) 88 | self.write_cmd(self.pages - 1) 89 | self.write_framebuf() 90 | 91 | def fill(self, col): 92 | self.framebuf.fill(col) 93 | 94 | def pixel(self, x, y, col): 95 | self.framebuf.pixel(x, y, col) 96 | 97 | def scroll(self, dx, dy): 98 | self.framebuf.scroll(dx, dy) 99 | 100 | def text(self, string, x, y, col=1): 101 | self.framebuf.text(string, x, y, col) 102 | 103 | 104 | class SSD1306_I2C(SSD1306): 105 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 106 | self.i2c = i2c 107 | self.addr = addr 108 | self.temp = bytearray(2) 109 | # Add an extra byte to the data buffer to hold an I2C data/command byte 110 | # to use hardware-compatible I2C transactions. A memoryview of the 111 | # buffer is used to mask this byte from the framebuffer operations 112 | # (without a major memory hit as memoryview doesn't copy to a separate 113 | # buffer). 114 | self.buffer = bytearray(((height // 8) * width) + 1) 115 | self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1 116 | self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height) 117 | super().__init__(width, height, external_vcc) 118 | 119 | def write_cmd(self, cmd): 120 | self.temp[0] = 0x80 # Co=1, D/C#=0 121 | self.temp[1] = cmd 122 | self.i2c.writeto(self.addr, self.temp) 123 | 124 | def write_framebuf(self): 125 | # Blast out the frame buffer using a single I2C transaction to support 126 | # hardware I2C interfaces. 127 | self.i2c.writeto(self.addr, self.buffer) 128 | 129 | def poweron(self): 130 | pass 131 | 132 | 133 | class SSD1306_SPI(SSD1306): 134 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 135 | self.rate = 10 * 1024 * 1024 136 | dc.init(dc.OUT, value=0) 137 | res.init(res.OUT, value=0) 138 | cs.init(cs.OUT, value=1) 139 | self.spi = spi 140 | self.dc = dc 141 | self.res = res 142 | self.cs = cs 143 | self.buffer = bytearray((height // 8) * width) 144 | self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height) 145 | super().__init__(width, height, external_vcc) 146 | 147 | def write_cmd(self, cmd): 148 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 149 | self.cs.high() 150 | self.dc.low() 151 | self.cs.low() 152 | self.spi.write(bytearray([cmd])) 153 | self.cs.high() 154 | 155 | def write_framebuf(self): 156 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 157 | self.cs.high() 158 | self.dc.high() 159 | self.cs.low() 160 | self.spi.write(self.buffer) 161 | self.cs.high() 162 | 163 | def poweron(self): 164 | self.res.high() 165 | time.sleep_ms(1) 166 | self.res.low() 167 | time.sleep_ms(10) 168 | self.res.high() 169 | -------------------------------------------------------------------------------- /v2/README.md: -------------------------------------------------------------------------------- 1 | ### 版本2 2 | 3 | 第一个版本的恐龙、仙人掌是用循环数组逐点像素实现的,每帧显示都重新绘制一边,这种绘制的算法没有优化,因此可以看到有卡顿。 4 | 5 | 版本2中,不再使用excel表格而是使用pbm格式的图片。 6 | 7 | 如下所示,这是把游戏玩家的像素图读出来。 8 | 9 | ``` 10 | with open('player.pbm', 'rb') as f: 11 | f.readline() # Magic number 12 | f.readline() # Creator comment 13 | f.readline() # Dimensions 14 | data = bytearray(f.read()) 15 | player["buf_jump"] = framebuf.FrameBuffer(data, 20, 20, framebuf.MONO_HLSB) 16 | ``` 17 | 18 | 到需要绘制的时候,无需循环像素,而是使用内置的函数来绘制。 19 | 20 | ``` 21 | oled.blit(player["buf_jump"], player["x"], player["y"]) 22 | ``` 23 | 24 | v2版本增加了暂停,其实这很容易,就是针对2个按钮来使用状态机: 25 | 26 | ``` 27 | def blue_click(): 28 | if(status["is_jumpfinish"]): 29 | status["is_jump"]=True 30 | status["is_jumpfinish"]=False 31 | 32 | def red_click(): 33 | if(status["game"]=="ready"): 34 | status["game"]="playing" 35 | elif(status["game"]=="playing"): 36 | status["game"]="pause" 37 | elif(status["game"]=="pause"): 38 | status["game"]="playing" 39 | elif(status["game"]=="gameover"): 40 | begin() 41 | status["game"]="playing" 42 | 43 | ``` 44 | 45 | 进行时的界面 46 | ![](../images/v2_playing.jpg) 47 | 48 | 暂停时的界面 49 | ![](../images/v2_pause.jpg) 50 | 51 | 游戏结束时的界面 52 | ![](../images/v2_gameover.jpg) 53 | 54 | 演示: 55 | https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/master/demo2.mp4 -------------------------------------------------------------------------------- /v2/bg.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/v2/bg.pbm -------------------------------------------------------------------------------- /v2/cacti.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/v2/cacti.pbm -------------------------------------------------------------------------------- /v2/gameover.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/v2/gameover.pbm -------------------------------------------------------------------------------- /v2/gfx.py: -------------------------------------------------------------------------------- 1 | # Port of Adafruit GFX Arduino library to MicroPython. 2 | # Based on: https://github.com/adafruit/Adafruit-GFX-Library 3 | # Author: Tony DiCola (original GFX author Phil Burgess) 4 | # License: MIT License (https://opensource.org/licenses/MIT) 5 | 6 | 7 | class GFX: 8 | 9 | def __init__(self, width, height, pixel, hline=None, vline=None): 10 | # Create an instance of the GFX drawing class. You must pass in the 11 | # following parameters: 12 | # - width = The width of the drawing area in pixels. 13 | # - height = The height of the drawing area in pixels. 14 | # - pixel = A function to call when a pixel is drawn on the display. 15 | # This function should take at least an x and y position 16 | # and then any number of optional color or other parameters. 17 | # You can also provide the following optional keyword argument to 18 | # improve the performance of drawing: 19 | # - hline = A function to quickly draw a horizontal line on the display. 20 | # This should take at least an x, y, and width parameter and 21 | # any number of optional color or other parameters. 22 | # - vline = A function to quickly draw a vertical line on the display. 23 | # This should take at least an x, y, and height paraemter and 24 | # any number of optional color or other parameters. 25 | self.width = width 26 | self.height = height 27 | self._pixel = pixel 28 | # Default to slow horizontal & vertical line implementations if no 29 | # faster versions are provided. 30 | if hline is None: 31 | self.hline = self._slow_hline 32 | else: 33 | self.hline = hline 34 | if vline is None: 35 | self.vline = self._slow_vline 36 | else: 37 | self.vline = vline 38 | 39 | def _slow_hline(self, x0, y0, width, *args, **kwargs): 40 | # Slow implementation of a horizontal line using pixel drawing. 41 | # This is used as the default horizontal line if no faster override 42 | # is provided. 43 | if y0 < 0 or y0 > self.height or x0 < -width or x0 > self.width: 44 | return 45 | for i in range(width): 46 | self._pixel(x0+i, y0, *args, **kwargs) 47 | 48 | def _slow_vline(self, x0, y0, height, *args, **kwargs): 49 | # Slow implementation of a vertical line using pixel drawing. 50 | # This is used as the default vertical line if no faster override 51 | # is provided. 52 | if y0 < -height or y0 > self.height or x0 < 0 or x0 > self.width: 53 | return 54 | for i in range(height): 55 | self._pixel(x0, y0+i, *args, **kwargs) 56 | 57 | def rect(self, x0, y0, width, height, *args, **kwargs): 58 | # Rectangle drawing function. Will draw a single pixel wide rectangle 59 | # starting in the upper left x0, y0 position and width, height pixels in 60 | # size. 61 | if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width: 62 | return 63 | self.hline(x0, y0, width, *args, **kwargs) 64 | self.hline(x0, y0+height-1, width, *args, **kwargs) 65 | self.vline(x0, y0, height, *args, **kwargs) 66 | self.vline(x0+width-1, y0, height, *args, **kwargs) 67 | 68 | def fill_rect(self, x0, y0, width, height, *args, **kwargs): 69 | # Filled rectangle drawing function. Will draw a single pixel wide 70 | # rectangle starting in the upper left x0, y0 position and width, height 71 | # pixels in size. 72 | if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width: 73 | return 74 | for i in range(x0, x0+width): 75 | self.vline(i, y0, height, *args, **kwargs) 76 | 77 | def line(self, x0, y0, x1, y1, *args, **kwargs): 78 | # Line drawing function. Will draw a single pixel wide line starting at 79 | # x0, y0 and ending at x1, y1. 80 | steep = abs(y1 - y0) > abs(x1 - x0) 81 | if steep: 82 | x0, y0 = y0, x0 83 | x1, y1 = y1, x1 84 | if x0 > x1: 85 | x0, x1 = x1, x0 86 | y0, y1 = y1, y0 87 | dx = x1 - x0 88 | dy = abs(y1 - y0) 89 | err = dx // 2 90 | ystep = 0 91 | if y0 < y1: 92 | ystep = 1 93 | else: 94 | ystep = -1 95 | while x0 <= x1: 96 | if steep: 97 | self._pixel(y0, x0, *args, **kwargs) 98 | else: 99 | self._pixel(x0, y0, *args, **kwargs) 100 | err -= dy 101 | if err < 0: 102 | y0 += ystep 103 | err += dx 104 | x0 += 1 105 | 106 | def circle(self, x0, y0, radius, *args, **kwargs): 107 | # Circle drawing function. Will draw a single pixel wide circle with 108 | # center at x0, y0 and the specified radius. 109 | f = 1 - radius 110 | ddF_x = 1 111 | ddF_y = -2 * radius 112 | x = 0 113 | y = radius 114 | self._pixel(x0, y0 + radius, *args, **kwargs) 115 | self._pixel(x0, y0 - radius, *args, **kwargs) 116 | self._pixel(x0 + radius, y0, *args, **kwargs) 117 | self._pixel(x0 - radius, y0, *args, **kwargs) 118 | while x < y: 119 | if f >= 0: 120 | y -= 1 121 | ddF_y += 2 122 | f += ddF_y 123 | x += 1 124 | ddF_x += 2 125 | f += ddF_x 126 | self._pixel(x0 + x, y0 + y, *args, **kwargs) 127 | self._pixel(x0 - x, y0 + y, *args, **kwargs) 128 | self._pixel(x0 + x, y0 - y, *args, **kwargs) 129 | self._pixel(x0 - x, y0 - y, *args, **kwargs) 130 | self._pixel(x0 + y, y0 + x, *args, **kwargs) 131 | self._pixel(x0 - y, y0 + x, *args, **kwargs) 132 | self._pixel(x0 + y, y0 - x, *args, **kwargs) 133 | self._pixel(x0 - y, y0 - x, *args, **kwargs) 134 | 135 | def fill_circle(self, x0, y0, radius, *args, **kwargs): 136 | # Filled circle drawing function. Will draw a filled circule with 137 | # center at x0, y0 and the specified radius. 138 | self.vline(x0, y0 - radius, 2*radius + 1, *args, **kwargs) 139 | f = 1 - radius 140 | ddF_x = 1 141 | ddF_y = -2 * radius 142 | x = 0 143 | y = radius 144 | while x < y: 145 | if f >= 0: 146 | y -= 1 147 | ddF_y += 2 148 | f += ddF_y 149 | x += 1 150 | ddF_x += 2 151 | f += ddF_x 152 | self.vline(x0 + x, y0 - y, 2*y + 1, *args, **kwargs) 153 | self.vline(x0 + y, y0 - x, 2*x + 1, *args, **kwargs) 154 | self.vline(x0 - x, y0 - y, 2*y + 1, *args, **kwargs) 155 | self.vline(x0 - y, y0 - x, 2*x + 1, *args, **kwargs) 156 | 157 | def triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs): 158 | # Triangle drawing function. Will draw a single pixel wide triangle 159 | # around the points (x0, y0), (x1, y1), and (x2, y2). 160 | self.line(x0, y0, x1, y1, *args, **kwargs) 161 | self.line(x1, y1, x2, y2, *args, **kwargs) 162 | self.line(x2, y2, x0, y0, *args, **kwargs) 163 | 164 | def fill_triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs): 165 | # Filled triangle drawing function. Will draw a filled triangle around 166 | # the points (x0, y0), (x1, y1), and (x2, y2). 167 | if y0 > y1: 168 | y0, y1 = y1, y0 169 | x0, x1 = x1, x0 170 | if y1 > y2: 171 | y2, y1 = y1, y2 172 | x2, x1 = x1, x2 173 | if y0 > y1: 174 | y0, y1 = y1, y0 175 | x0, x1 = x1, x0 176 | a = 0 177 | b = 0 178 | y = 0 179 | last = 0 180 | if y0 == y2: 181 | a = x0 182 | b = x0 183 | if x1 < a: 184 | a = x1 185 | elif x1 > b: 186 | b = x1 187 | if x2 < a: 188 | a = x2 189 | elif x2 > b: 190 | b = x2 191 | self.hline(a, y0, b-a+1, *args, **kwargs) 192 | return 193 | dx01 = x1 - x0 194 | dy01 = y1 - y0 195 | dx02 = x2 - x0 196 | dy02 = y2 - y0 197 | dx12 = x2 - x1 198 | dy12 = y2 - y1 199 | if dy01 == 0: 200 | dy01 = 1 201 | if dy02 == 0: 202 | dy02 = 1 203 | if dy12 == 0: 204 | dy12 = 1 205 | sa = 0 206 | sb = 0 207 | if y1 == y2: 208 | last = y1 209 | else: 210 | last = y1-1 211 | for y in range(y0, last+1): 212 | a = x0 + sa // dy01 213 | b = x0 + sb // dy02 214 | sa += dx01 215 | sb += dx02 216 | if a > b: 217 | a, b = b, a 218 | self.hline(a, y, b-a+1, *args, **kwargs) 219 | sa = dx12 * (y - y1) 220 | sb = dx02 * (y - y0) 221 | while y <= y2: 222 | a = x1 + sa // dy12 223 | b = x0 + sb // dy02 224 | sa += dx12 225 | sb += dx02 226 | if a > b: 227 | a, b = b, a 228 | self.hline(a, y, b-a+1, *args, **kwargs) 229 | y += 1 230 | -------------------------------------------------------------------------------- /v2/main.py: -------------------------------------------------------------------------------- 1 | from machine import Pin,I2C,SPI 2 | import ssd1306 3 | import gfx 4 | from time import sleep 5 | import framebuf 6 | pin_blue = Pin(4, Pin.IN) 7 | pin_red = Pin(5, Pin.IN) 8 | 9 | i2c = I2C(scl=Pin(2), sda=Pin(0), freq=100000) 10 | oled = ssd1306.SSD1306_I2C(128,64, i2c) 11 | 12 | #oled = ssd1306.SSD1306_I2C(128, 32, i2c, addr=0x27) 13 | graphics = gfx.GFX(128, 64, oled.pixel) 14 | oled.poweron() 15 | oled.init_display() 16 | oled.fill(0) 17 | 18 | oled.show() 19 | 20 | def blue_click(): 21 | if(status["is_jumpfinish"]): 22 | status["is_jump"]=True 23 | status["is_jumpfinish"]=False 24 | 25 | 26 | def red_click(): 27 | if(status["game"]=="ready"): 28 | status["game"]="playing" 29 | elif(status["game"]=="playing"): 30 | status["game"]="pause" 31 | elif(status["game"]=="pause"): 32 | status["game"]="playing" 33 | elif(status["game"]=="gameover"): 34 | begin() 35 | status["game"]="playing" 36 | 37 | def fire(): 38 | pass 39 | 40 | status={} 41 | 42 | status["game"]="loading" 43 | 44 | status["gametime"]=0 45 | status["km"]=0 46 | 47 | status["is_jump"]=False 48 | status["is_fire"]=False 49 | status["is_jumpfinish"]=True 50 | 51 | 52 | 53 | 54 | 55 | 56 | player = {} 57 | player["x"] = 10 58 | player["y"] = 44 59 | player["leg_status"]="1" 60 | 61 | 62 | with open('player.pbm', 'rb') as f: 63 | f.readline() # Magic number 64 | f.readline() # Creator comment 65 | f.readline() # Dimensions 66 | data = bytearray(f.read()) 67 | player["buf_jump"] = framebuf.FrameBuffer(data, 20, 20, framebuf.MONO_HLSB) 68 | 69 | with open('player1.pbm', 'rb') as f: 70 | f.readline() # Magic number 71 | f.readline() # Creator comment 72 | f.readline() # Dimensions 73 | data = bytearray(f.read()) 74 | player["buf1"] = framebuf.FrameBuffer(data, 20, 20, framebuf.MONO_HLSB) 75 | 76 | with open('player2.pbm', 'rb') as f: 77 | f.readline() # Magic number 78 | f.readline() # Creator comment 79 | f.readline() # Dimensions 80 | data = bytearray(f.read()) 81 | player["buf2"] = framebuf.FrameBuffer(data, 20, 20, framebuf.MONO_HLSB) 82 | player["buf"] = player["buf_jump"] 83 | 84 | obj={} 85 | obj["x"] = 130 86 | obj["y"] = 44 87 | with open('cacti.pbm', 'rb') as f: 88 | f.readline() # Magic number 89 | f.readline() # Creator comment 90 | f.readline() # Dimensions 91 | data = bytearray(f.read()) 92 | obj["buf"] = framebuf.FrameBuffer(data, 10, 20, framebuf.MONO_HLSB) 93 | 94 | bg={} 95 | bg["x"] = 0 96 | bg["y"] = 53 97 | with open('bg.pbm', 'rb') as f: 98 | f.readline() # Magic number 99 | f.readline() # Creator comment 100 | f.readline() # Dimensions 101 | data = bytearray(f.read()) 102 | bg["buf"] = framebuf.FrameBuffer(data, 256, 10, framebuf.MONO_HLSB) 103 | 104 | with open('gameover.pbm', 'rb') as f: 105 | f.readline() # Magic number 106 | f.readline() # Creator comment 107 | f.readline() # Dimensions 108 | data = bytearray(f.read()) 109 | gameover_buf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB) 110 | 111 | 112 | def begin(): 113 | status["game"]="loading" 114 | status["gametime"]=0 115 | status["km"]=0 116 | status["is_jump"]=False 117 | status["is_fire"]=False 118 | status["is_jumpfinish"]=True 119 | 120 | obj["x"] = 130 121 | obj["y"] = 44 122 | player["y"] = 44 123 | 124 | 125 | def draw_player(): 126 | if(status["is_jump"]): 127 | player["y"]-=3 128 | oled.blit(player["buf_jump"], player["x"], player["y"]) 129 | if(player["y"]<15): 130 | status["is_jump"]=False 131 | else: 132 | player["buf"] = player["buf1"] 133 | player["y"]+=3 134 | 135 | if(player["y"]>=43): 136 | player["y"]=43 137 | status["is_jumpfinish"]=True 138 | if (player["leg_status"]=="1" ): 139 | oled.blit(player["buf1"], player["x"], player["y"]) 140 | player["leg_status"]="2" 141 | elif (player["leg_status"]=="2" ): 142 | oled.blit(player["buf2"], player["x"], player["y"]) 143 | player["leg_status"]="1" 144 | 145 | def draw_obj(): 146 | obj["x"]-=4 147 | 148 | oled.blit(obj["buf"], obj["x"], obj["y"]) 149 | 150 | if(obj["x"]<=-10): 151 | obj["x"]=130 152 | 153 | def draw_bg(): 154 | bg["x"]-=4 155 | 156 | oled.blit(bg["buf"], bg["x"], bg["y"]) 157 | oled.text("{0} km".format(status["km"]),2,0) 158 | if(bg["x"]<=-10): 159 | bg["x"]=0 160 | 161 | def check(): 162 | if(obj["x"]-player["x"]<15 and obj["y"]-player["y"]<15): 163 | status["game"]="gameover" 164 | pass 165 | # pixels_a = player["pixel"] 166 | # pixels_b = obj["pixel"] 167 | 168 | 169 | while (True): 170 | oled.fill(0) 171 | oled.contrast(1) 172 | 173 | blue = pin_blue.value() 174 | red = pin_red.value() 175 | 176 | if(pin_red.value() == 1): 177 | red_click() 178 | if(status["game"]=="loading"): 179 | oled.text("loading.".format(status["km"]),10,30) 180 | oled.show() 181 | sleep(1) 182 | oled.text("loading..".format(status["km"]),10,30) 183 | oled.show() 184 | sleep(1) 185 | oled.text("loading...".format(status["km"]),10,30) 186 | oled.show() 187 | status["game"]="ready" 188 | elif(status["game"]=="ready"): 189 | oled.text("> play".format(status["km"]),10,20) 190 | oled.text("code by".format(status["km"]),10,30) 191 | oled.text("cr4fun".format(status["km"]),10,40) 192 | elif(status["game"]=="pause"): 193 | oled.text("pause",25,30) 194 | elif(status["game"]=="playing"): 195 | if(pin_blue.value() == 1): 196 | blue_click() 197 | status["km"]+=1 198 | status["gametime"]+=1 199 | #graphics.line(0, 63, 127, 63, 1) 200 | draw_bg() 201 | draw_player() 202 | draw_obj() 203 | check() 204 | elif(status["game"]=="gameover"): 205 | oled.text("{0} km".format(status["km"]),2,0) 206 | #oled.text("game over",25,30) 207 | oled.blit(gameover_buf, 0,25) 208 | oled.show() 209 | 210 | -------------------------------------------------------------------------------- /v2/player.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/v2/player.pbm -------------------------------------------------------------------------------- /v2/player1.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/v2/player1.pbm -------------------------------------------------------------------------------- /v2/player2.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpensourceBooks/chrome_offline_game_on_esp8266/37f7014e5d398620f955953e63e558b885d87554/v2/player2.pbm -------------------------------------------------------------------------------- /v2/ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | 3 | import time 4 | import framebuf 5 | 6 | 7 | # register definitions 8 | SET_CONTRAST = const(0x81) 9 | SET_ENTIRE_ON = const(0xa4) 10 | SET_NORM_INV = const(0xa6) 11 | SET_DISP = const(0xae) 12 | SET_MEM_ADDR = const(0x20) 13 | SET_COL_ADDR = const(0x21) 14 | SET_PAGE_ADDR = const(0x22) 15 | SET_DISP_START_LINE = const(0x40) 16 | SET_SEG_REMAP = const(0xa0) 17 | SET_MUX_RATIO = const(0xa8) 18 | SET_COM_OUT_DIR = const(0xc0) 19 | SET_DISP_OFFSET = const(0xd3) 20 | SET_COM_PIN_CFG = const(0xda) 21 | SET_DISP_CLK_DIV = const(0xd5) 22 | SET_PRECHARGE = const(0xd9) 23 | SET_VCOM_DESEL = const(0xdb) 24 | SET_CHARGE_PUMP = const(0x8d) 25 | 26 | 27 | class SSD1306: 28 | def __init__(self, width, height, external_vcc): 29 | self.width = width 30 | self.height = height 31 | self.external_vcc = external_vcc 32 | self.pages = self.height // 8 33 | # Note the subclass must initialize self.framebuf to a framebuffer. 34 | # This is necessary because the underlying data buffer is different 35 | # between I2C and SPI implementations (I2C needs an extra byte). 36 | self.poweron() 37 | self.init_display() 38 | 39 | def init_display(self): 40 | for cmd in ( 41 | SET_DISP | 0x00, # off 42 | # address setting 43 | SET_MEM_ADDR, 0x00, # horizontal 44 | # resolution and layout 45 | SET_DISP_START_LINE | 0x00, 46 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 47 | SET_MUX_RATIO, self.height - 1, 48 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 49 | SET_DISP_OFFSET, 0x00, 50 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 51 | # timing and driving scheme 52 | SET_DISP_CLK_DIV, 0x80, 53 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 54 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 55 | # display 56 | SET_CONTRAST, 0xff, # maximum 57 | SET_ENTIRE_ON, # output follows RAM contents 58 | SET_NORM_INV, # not inverted 59 | # charge pump 60 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 61 | SET_DISP | 0x01): # on 62 | self.write_cmd(cmd) 63 | self.fill(0) 64 | self.show() 65 | 66 | def poweroff(self): 67 | self.write_cmd(SET_DISP | 0x00) 68 | 69 | def contrast(self, contrast): 70 | self.write_cmd(SET_CONTRAST) 71 | self.write_cmd(contrast) 72 | 73 | def invert(self, invert): 74 | self.write_cmd(SET_NORM_INV | (invert & 1)) 75 | 76 | def show(self): 77 | x0 = 0 78 | x1 = self.width - 1 79 | if self.width == 64: 80 | # displays with width of 64 pixels are shifted by 32 81 | x0 += 32 82 | x1 += 32 83 | self.write_cmd(SET_COL_ADDR) 84 | self.write_cmd(x0) 85 | self.write_cmd(x1) 86 | self.write_cmd(SET_PAGE_ADDR) 87 | self.write_cmd(0) 88 | self.write_cmd(self.pages - 1) 89 | self.write_framebuf() 90 | 91 | def fill(self, col): 92 | self.framebuf.fill(col) 93 | 94 | def pixel(self, x, y, col): 95 | self.framebuf.pixel(x, y, col) 96 | 97 | def scroll(self, dx, dy): 98 | self.framebuf.scroll(dx, dy) 99 | 100 | def text(self, string, x, y, col=1): 101 | self.framebuf.text(string, x, y, col) 102 | 103 | 104 | class SSD1306_I2C(SSD1306): 105 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 106 | self.i2c = i2c 107 | self.addr = addr 108 | self.temp = bytearray(2) 109 | # Add an extra byte to the data buffer to hold an I2C data/command byte 110 | # to use hardware-compatible I2C transactions. A memoryview of the 111 | # buffer is used to mask this byte from the framebuffer operations 112 | # (without a major memory hit as memoryview doesn't copy to a separate 113 | # buffer). 114 | self.buffer = bytearray(((height // 8) * width) + 1) 115 | self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1 116 | self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height) 117 | super().__init__(width, height, external_vcc) 118 | 119 | def write_cmd(self, cmd): 120 | self.temp[0] = 0x80 # Co=1, D/C#=0 121 | self.temp[1] = cmd 122 | self.i2c.writeto(self.addr, self.temp) 123 | 124 | def write_framebuf(self): 125 | # Blast out the frame buffer using a single I2C transaction to support 126 | # hardware I2C interfaces. 127 | self.i2c.writeto(self.addr, self.buffer) 128 | 129 | def poweron(self): 130 | pass 131 | 132 | 133 | class SSD1306_SPI(SSD1306): 134 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 135 | self.rate = 10 * 1024 * 1024 136 | dc.init(dc.OUT, value=0) 137 | res.init(res.OUT, value=0) 138 | cs.init(cs.OUT, value=1) 139 | self.spi = spi 140 | self.dc = dc 141 | self.res = res 142 | self.cs = cs 143 | self.buffer = bytearray((height // 8) * width) 144 | self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height) 145 | super().__init__(width, height, external_vcc) 146 | 147 | def write_cmd(self, cmd): 148 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 149 | self.cs.high() 150 | self.dc.low() 151 | self.cs.low() 152 | self.spi.write(bytearray([cmd])) 153 | self.cs.high() 154 | 155 | def write_framebuf(self): 156 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 157 | self.cs.high() 158 | self.dc.high() 159 | self.cs.low() 160 | self.spi.write(self.buffer) 161 | self.cs.high() 162 | 163 | def poweron(self): 164 | self.res.high() 165 | time.sleep_ms(1) 166 | self.res.low() 167 | time.sleep_ms(10) 168 | self.res.high() 169 | --------------------------------------------------------------------------------