赞
踩
来自:https://github.com/zelacerda/micropython
代码改造了一下,让它可以跑起来。
简单分析一下代码。外层是一个死循环,有一个状态机来对应不同的场景。
- def loop():
- while True:
- if state == 0: splash_screen()
- elif state == 1: game_waiting()
- elif state == 2: game_running()
- elif state == 3: game_over()
0是最开始的场景,通过检查按键进行切换。
button = Pin(13, Pin.IN)
按键之后切换到running。
- def game_running():
- global state
- if clicked(): flappy_bird.flap()
- flappy_bird.move()
- if flappy_bird.crashed():
- flappy_bird.y = HEIGHT - flappy_bird.height
- state = 3
- obstacle_1.scroll()
- obstacle_2.scroll()
- if obstacle_1.collided(flappy_bird.y) or obstacle_2.collided(flappy_bird.y):
- state = 3
- draw()
看了running其实就明白,所有的操作,以帧为单位。
进入这一帧时,首先根据按键来更新小鸟的位置,然后判断小鸟是否飞出去了。
然后更新障碍物的位置,然后判断障碍物和小鸟是否碰撞。
更新位置之后,在Framebuffer中进行更新。
显示的部分,还有挺有趣。小鸟,障碍物是这样表示的,是三个字符串:
- # Bitmap images
- BIRD = '07e018f021f871ecf9ecfcfcbe7e4c81717e4082307c0f80'
- COL1 = '201c'*26+'ffff'+'800f'*4+'ffff'
- COL2 = 'ffff'+'800f'*4+'ffff'+'201c'*26
然后基于这三个字符串创建的三个Framebuffer对象。
显示的时候一次调用blit方法加进去,有点类似memcpy。Framebuffer 的 blit 方法是用于将一个区域的图像数据从一个帧缓冲区复制到另一个帧缓冲区的方法。在图形编程中,blit 是 "block transfer"(块传输)的简称,通常用于在内存中进行图像数据的复制或传输操作。
在上面一旦检测到飞出画面或者碰到障碍物,就进到最后一个场景,结束画面。如果没问题就让屏幕正常显示。这个倒是没啥好说的。
可以看出,常规游戏的实现思路,就是基于2D图形的,以帧为单位,所有的逻辑操作都是基于2D的坐标体系。显示方面封装后就直接看做一块内存,逻辑处理完之后控制内存中数据的变化,最后把改动提交到显示器。
改造后完整代码如下:
game.py
- '''
- Flappy Bird for ESP8266 modules
- github.com/zelacerda/micropython
- Version 1.0
- 2017 - by zelacerda
- '''
-
- import ssd1306
- from framebuf import FrameBuffer as FB
- from machine import I2C, Pin
- from utime import sleep
- import time
- #from urequests import post
-
- # Screen dimensions
- WIDTH = 128
- HEIGHT = 64
-
- # Initialize pins
- i2c = I2C(1, scl=Pin(7), sda=Pin(6))
- oled = ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)
- button = Pin(13, Pin.IN)
-
- # Some helper functions
- def random(a,b):
- seed = int(time.time() * 1000) # 将当前时间转换为毫秒级别的整数
- # 利用种子生成伪随机数
- random_num = (seed * 1103515245 + 12345) % (2**31)
- # 将随机数映射到指定范围[a, b]
- return random_num % (b - a + 1) + a
-
- def to_bytearray(s):
- return bytearray([int('0x'+s[i:i+2]) for i in range(0,len(s),2)])
-
- def write_high_score(n):
- f = open('fb_high_score', 'w')
- f.write(str(n))
- f.close()
-
- def read_high_score():
- return 0
-
- def send_score(n):
- url = "http://things.ubidots.com/api/v1.6/devices/NodeMCU?token="
- token = "A1E-5ZY9vbCGtRiqVinrnhrQxgA4FDSBaA"
- url += token
- headers = {"Content-Type": "application/json"}
- data = '{"flappy-bird-score": ' + str(n) + '}'
- #post(url, data=data, headers=headers)
-
- # Bitmap images
- BIRD = '07e018f021f871ecf9ecfcfcbe7e4c81717e4082307c0f80'
- COL1 = '201c'*26+'ffff'+'800f'*4+'ffff'
- COL2 = 'ffff'+'800f'*4+'ffff'+'201c'*26
- bird_size = (16,12)
- colu_size = (16,32)
-
- # Generate sprites
- bird = FB(to_bytearray(BIRD),bird_size[0],bird_size[1],3)
- col1 = FB(to_bytearray(COL1),colu_size[0],colu_size[1],3)
- col2 = FB(to_bytearray(COL2),colu_size[0],colu_size[1],3)
-
- class FlappyBird:
- def __init__(self):
- self.height = bird_size[1]
- self.y = HEIGHT // 2 - self.height // 2
- self.vel = -wing_power
-
- def move(self):
- self.vel += gravity
- self.y = int(self.y + self.vel)
-
- def flap(self):
- self.vel = -wing_power
-
- def crashed(self):
- y_limit = HEIGHT - self.height
- return self.y > y_limit
-
- class Obstacle:
- def __init__(self, x):
- self.gap = random(6+gap_size, HEIGHT-6-gap_size)
- self.x = x
- self.score = 0
-
- def scroll(self):
- self.x -= velocity
- if self.x < -colu_size[0]:
- self.score += 1
- self.x = WIDTH
- self.gap = random(6+gap_size, HEIGHT-6-gap_size)
-
- def collided(self, y):
- if self.x < bird_size[0] and \
- self.x > -colu_size[0] and \
- (self.gap - y > gap_size or y + bird_size[1] - self.gap > gap_size):
- return True
- else:
- return False
-
- def clicked():
- global pressed
- if button.value() == 1 and not pressed:
- pressed = True
- return True
- elif button.value() == 0 and pressed:
- pressed = False
- return False
-
- def draw():
- oled.fill(0)
- oled.blit(bird, 0, flappy_bird.y)
- oled.blit(col1,obstacle_1.x,obstacle_1.gap-gap_size-colu_size[1])
- oled.blit(col2,obstacle_1.x,obstacle_1.gap+gap_size)
- oled.blit(col1,obstacle_2.x,obstacle_2.gap-gap_size-colu_size[1])
- oled.blit(col2,obstacle_2.x,obstacle_2.gap+gap_size)
- oled.fill_rect(WIDTH//2 - 13, 0, 26, 9, 0)
- oled.text('%03d' % (obstacle_1.score + obstacle_2.score), WIDTH//2 - 12, 0)
- oled.show()
-
- # Game parameters
- high_score = read_high_score()
- gap_size = 13
- velocity = 3
- gravity = .8
- wing_power = 4
- state = 0
- pressed = False
-
- # Game state functions
- def splash_screen():
- global state
- oled.fill(0)
- oled.blit(col2, (WIDTH-colu_size[0])//2, HEIGHT-12)
- oled.blit(bird, (WIDTH-bird_size[0])//2, HEIGHT-12-bird_size[1])
- oled.rect(0, 0, WIDTH, HEIGHT, 1)
- oled.text('F L A P P Y', WIDTH//2-44, 3)
- oled.text('B I R D', WIDTH//2-28, 13)
- oled.text('Record: ' + '%03d' % high_score, WIDTH//2-44, HEIGHT//2-6)
- oled.show()
- state = 1
-
- def game_waiting():
- global state,score,flappy_bird,obstacle_1,obstacle_2, pressed
- if clicked():
- flappy_bird = FlappyBird()
- obstacle_1 = Obstacle(WIDTH)
- obstacle_2 = Obstacle(WIDTH + (WIDTH + colu_size[0]) // 2)
- state = 2
-
- def game_running():
- global state
- if clicked(): flappy_bird.flap()
- flappy_bird.move()
- if flappy_bird.crashed():
- flappy_bird.y = HEIGHT - flappy_bird.height
- state = 3
- obstacle_1.scroll()
- obstacle_2.scroll()
- if obstacle_1.collided(flappy_bird.y) or obstacle_2.collided(flappy_bird.y):
- state = 3
- draw()
-
- def game_over():
- global state, high_score
- oled.fill_rect(WIDTH//2-32, 10, 64, 23, 0)
- oled.rect(WIDTH//2-32, 10, 64, 23, 1)
- oled.text('G A M E', WIDTH//2-28, 13)
- oled.text('O V E R', WIDTH//2-28, 23)
- score = obstacle_1.score + obstacle_2.score
- if score > high_score:
- high_score = score
- oled.fill_rect(WIDTH//2-48, 37, 96, 14, 0)
- oled.rect(WIDTH//2-48, 37, 96, 14, 1)
- oled.text('New record!',WIDTH//2-44, 40)
- write_high_score(high_score)
- oled.show()
- try:
- send_score(score)
- except:
- pass
- state = 1
-
- def loop():
- while True:
- if state == 0: splash_screen()
- elif state == 1: game_waiting()
- elif state == 2: game_running()
- elif state == 3: game_over()
-
- loop()
oled驱动,ssd1306.py
- # MicroPython SSD1306 OLED driver, I2C and SPI interfaces
-
- from micropython import const
- import framebuf
-
-
- # register definitions
- SET_CONTRAST = const(0x81)
- SET_ENTIRE_ON = const(0xA4)
- SET_NORM_INV = const(0xA6)
- SET_DISP = const(0xAE)
- SET_MEM_ADDR = const(0x20)
- SET_COL_ADDR = const(0x21)
- SET_PAGE_ADDR = const(0x22)
- SET_DISP_START_LINE = const(0x40)
- SET_SEG_REMAP = const(0xA0)
- SET_MUX_RATIO = const(0xA8)
- SET_COM_OUT_DIR = const(0xC0)
- SET_DISP_OFFSET = const(0xD3)
- SET_COM_PIN_CFG = const(0xDA)
- SET_DISP_CLK_DIV = const(0xD5)
- SET_PRECHARGE = const(0xD9)
- SET_VCOM_DESEL = const(0xDB)
- SET_CHARGE_PUMP = const(0x8D)
-
- # Subclassing FrameBuffer provides support for graphics primitives
- # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
- class SSD1306(framebuf.FrameBuffer):
- def __init__(self, width, height, external_vcc):
- self.width = width
- self.height = height
- self.external_vcc = external_vcc
- self.pages = self.height // 8
- self.buffer = bytearray(self.pages * self.width)
- super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
- self.init_display()
-
- def init_display(self):
- for cmd in (
- SET_DISP | 0x00, # off
- # address setting
- SET_MEM_ADDR,
- 0x00, # horizontal
- # resolution and layout
- SET_DISP_START_LINE | 0x00,
- SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
- SET_MUX_RATIO,
- self.height - 1,
- SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
- SET_DISP_OFFSET,
- 0x00,
- SET_COM_PIN_CFG,
- 0x02 if self.width > 2 * self.height else 0x12,
- # timing and driving scheme
- SET_DISP_CLK_DIV,
- 0x80,
- SET_PRECHARGE,
- 0x22 if self.external_vcc else 0xF1,
- SET_VCOM_DESEL,
- 0x30, # 0.83*Vcc
- # display
- SET_CONTRAST,
- 0xFF, # maximum
- SET_ENTIRE_ON, # output follows RAM contents
- SET_NORM_INV, # not inverted
- # charge pump
- SET_CHARGE_PUMP,
- 0x10 if self.external_vcc else 0x14,
- SET_DISP | 0x01,
- ): # on
- self.write_cmd(cmd)
- self.fill(0)
- self.show()
-
- def poweroff(self):
- self.write_cmd(SET_DISP | 0x00)
-
- def poweron(self):
- self.write_cmd(SET_DISP | 0x01)
-
- def contrast(self, contrast):
- self.write_cmd(SET_CONTRAST)
- self.write_cmd(contrast)
-
- def invert(self, invert):
- self.write_cmd(SET_NORM_INV | (invert & 1))
-
- def show(self):
- x0 = 0
- x1 = self.width - 1
- if self.width == 64:
- # displays with width of 64 pixels are shifted by 32
- x0 += 32
- x1 += 32
- self.write_cmd(SET_COL_ADDR)
- self.write_cmd(x0)
- self.write_cmd(x1)
- self.write_cmd(SET_PAGE_ADDR)
- self.write_cmd(0)
- self.write_cmd(self.pages - 1)
- self.write_data(self.buffer)
-
-
- class SSD1306_I2C(SSD1306):
- def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
- self.i2c = i2c
- self.addr = addr
- self.temp = bytearray(2)
- self.write_list = [b"\x40", None] # Co=0, D/C#=1
- super().__init__(width, height, external_vcc)
-
- def write_cmd(self, cmd):
- self.temp[0] = 0x80 # Co=1, D/C#=0
- self.temp[1] = cmd
- self.i2c.writeto(self.addr, self.temp)
-
- def write_data(self, buf):
- self.write_list[1] = buf
- self.i2c.writevto(self.addr, self.write_list)
-
-
- class SSD1306_SPI(SSD1306):
- def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
- self.rate = 10 * 1024 * 1024
- dc.init(dc.OUT, value=0)
- res.init(res.OUT, value=0)
- cs.init(cs.OUT, value=1)
- self.spi = spi
- self.dc = dc
- self.res = res
- self.cs = cs
- import time
-
- self.res(1)
- time.sleep_ms(1)
- self.res(0)
- time.sleep_ms(10)
- self.res(1)
- super().__init__(width, height, external_vcc)
-
- def write_cmd(self, cmd):
- self.spi.init(baudrate=self.rate, polarity=0, phase=0)
- self.cs(1)
- self.dc(0)
- self.cs(0)
- self.spi.write(bytearray([cmd]))
- self.cs(1)
-
- def write_data(self, buf):
- self.spi.init(baudrate=self.rate, polarity=0, phase=0)
- self.cs(1)
- self.dc(1)
- self.cs(0)
- self.spi.write(buf)
- self.cs(1)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。