当前位置:   article > 正文

显示学习4(基于树莓派Pico) -- 游戏

显示学习4(基于树莓派Pico) -- 游戏

来自:https://github.com/zelacerda/micropython

代码改造了一下,让它可以跑起来。

简单分析一下代码。外层是一个死循环,有一个状态机来对应不同的场景。

  1. def loop():
  2. while True:
  3. if state == 0: splash_screen()
  4. elif state == 1: game_waiting()
  5. elif state == 2: game_running()
  6. elif state == 3: game_over()

0是最开始的场景,通过检查按键进行切换。

button = Pin(13, Pin.IN)

按键之后切换到running。

  1. def game_running():
  2. global state
  3. if clicked(): flappy_bird.flap()
  4. flappy_bird.move()
  5. if flappy_bird.crashed():
  6. flappy_bird.y = HEIGHT - flappy_bird.height
  7. state = 3
  8. obstacle_1.scroll()
  9. obstacle_2.scroll()
  10. if obstacle_1.collided(flappy_bird.y) or obstacle_2.collided(flappy_bird.y):
  11. state = 3
  12. draw()

看了running其实就明白,所有的操作,以帧为单位

进入这一帧时,首先根据按键来更新小鸟的位置,然后判断小鸟是否飞出去了。

然后更新障碍物的位置,然后判断障碍物和小鸟是否碰撞。

更新位置之后,在Framebuffer中进行更新。

显示的部分,还有挺有趣。小鸟,障碍物是这样表示的,是三个字符串

  1. # Bitmap images
  2. BIRD = '07e018f021f871ecf9ecfcfcbe7e4c81717e4082307c0f80'
  3. COL1 = '201c'*26+'ffff'+'800f'*4+'ffff'
  4. COL2 = 'ffff'+'800f'*4+'ffff'+'201c'*26

然后基于这三个字符串创建的三个Framebuffer对象。

显示的时候一次调用blit方法加进去,有点类似memcpy。Framebuffer 的 blit 方法是用于将一个区域的图像数据从一个帧缓冲区复制到另一个帧缓冲区的方法。在图形编程中,blit 是 "block transfer"(块传输)的简称,通常用于在内存中进行图像数据的复制或传输操作。

在上面一旦检测到飞出画面或者碰到障碍物,就进到最后一个场景,结束画面。如果没问题就让屏幕正常显示。这个倒是没啥好说的。

可以看出,常规游戏的实现思路,就是基于2D图形的,以帧为单位,所有的逻辑操作都是基于2D的坐标体系。显示方面封装后就直接看做一块内存,逻辑处理完之后控制内存中数据的变化,最后把改动提交到显示器。

改造后完整代码如下:

game.py

  1. '''
  2. Flappy Bird for ESP8266 modules
  3. github.com/zelacerda/micropython
  4. Version 1.0
  5. 2017 - by zelacerda
  6. '''
  7. import ssd1306
  8. from framebuf import FrameBuffer as FB
  9. from machine import I2C, Pin
  10. from utime import sleep
  11. import time
  12. #from urequests import post
  13. # Screen dimensions
  14. WIDTH = 128
  15. HEIGHT = 64
  16. # Initialize pins
  17. i2c = I2C(1, scl=Pin(7), sda=Pin(6))
  18. oled = ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)
  19. button = Pin(13, Pin.IN)
  20. # Some helper functions
  21. def random(a,b):
  22. seed = int(time.time() * 1000) # 将当前时间转换为毫秒级别的整数
  23. # 利用种子生成伪随机数
  24. random_num = (seed * 1103515245 + 12345) % (2**31)
  25. # 将随机数映射到指定范围[a, b]
  26. return random_num % (b - a + 1) + a
  27. def to_bytearray(s):
  28. return bytearray([int('0x'+s[i:i+2]) for i in range(0,len(s),2)])
  29. def write_high_score(n):
  30. f = open('fb_high_score', 'w')
  31. f.write(str(n))
  32. f.close()
  33. def read_high_score():
  34. return 0
  35. def send_score(n):
  36. url = "http://things.ubidots.com/api/v1.6/devices/NodeMCU?token="
  37. token = "A1E-5ZY9vbCGtRiqVinrnhrQxgA4FDSBaA"
  38. url += token
  39. headers = {"Content-Type": "application/json"}
  40. data = '{"flappy-bird-score": ' + str(n) + '}'
  41. #post(url, data=data, headers=headers)
  42. # Bitmap images
  43. BIRD = '07e018f021f871ecf9ecfcfcbe7e4c81717e4082307c0f80'
  44. COL1 = '201c'*26+'ffff'+'800f'*4+'ffff'
  45. COL2 = 'ffff'+'800f'*4+'ffff'+'201c'*26
  46. bird_size = (16,12)
  47. colu_size = (16,32)
  48. # Generate sprites
  49. bird = FB(to_bytearray(BIRD),bird_size[0],bird_size[1],3)
  50. col1 = FB(to_bytearray(COL1),colu_size[0],colu_size[1],3)
  51. col2 = FB(to_bytearray(COL2),colu_size[0],colu_size[1],3)
  52. class FlappyBird:
  53. def __init__(self):
  54. self.height = bird_size[1]
  55. self.y = HEIGHT // 2 - self.height // 2
  56. self.vel = -wing_power
  57. def move(self):
  58. self.vel += gravity
  59. self.y = int(self.y + self.vel)
  60. def flap(self):
  61. self.vel = -wing_power
  62. def crashed(self):
  63. y_limit = HEIGHT - self.height
  64. return self.y > y_limit
  65. class Obstacle:
  66. def __init__(self, x):
  67. self.gap = random(6+gap_size, HEIGHT-6-gap_size)
  68. self.x = x
  69. self.score = 0
  70. def scroll(self):
  71. self.x -= velocity
  72. if self.x < -colu_size[0]:
  73. self.score += 1
  74. self.x = WIDTH
  75. self.gap = random(6+gap_size, HEIGHT-6-gap_size)
  76. def collided(self, y):
  77. if self.x < bird_size[0] and \
  78. self.x > -colu_size[0] and \
  79. (self.gap - y > gap_size or y + bird_size[1] - self.gap > gap_size):
  80. return True
  81. else:
  82. return False
  83. def clicked():
  84. global pressed
  85. if button.value() == 1 and not pressed:
  86. pressed = True
  87. return True
  88. elif button.value() == 0 and pressed:
  89. pressed = False
  90. return False
  91. def draw():
  92. oled.fill(0)
  93. oled.blit(bird, 0, flappy_bird.y)
  94. oled.blit(col1,obstacle_1.x,obstacle_1.gap-gap_size-colu_size[1])
  95. oled.blit(col2,obstacle_1.x,obstacle_1.gap+gap_size)
  96. oled.blit(col1,obstacle_2.x,obstacle_2.gap-gap_size-colu_size[1])
  97. oled.blit(col2,obstacle_2.x,obstacle_2.gap+gap_size)
  98. oled.fill_rect(WIDTH//2 - 13, 0, 26, 9, 0)
  99. oled.text('%03d' % (obstacle_1.score + obstacle_2.score), WIDTH//2 - 12, 0)
  100. oled.show()
  101. # Game parameters
  102. high_score = read_high_score()
  103. gap_size = 13
  104. velocity = 3
  105. gravity = .8
  106. wing_power = 4
  107. state = 0
  108. pressed = False
  109. # Game state functions
  110. def splash_screen():
  111. global state
  112. oled.fill(0)
  113. oled.blit(col2, (WIDTH-colu_size[0])//2, HEIGHT-12)
  114. oled.blit(bird, (WIDTH-bird_size[0])//2, HEIGHT-12-bird_size[1])
  115. oled.rect(0, 0, WIDTH, HEIGHT, 1)
  116. oled.text('F L A P P Y', WIDTH//2-44, 3)
  117. oled.text('B I R D', WIDTH//2-28, 13)
  118. oled.text('Record: ' + '%03d' % high_score, WIDTH//2-44, HEIGHT//2-6)
  119. oled.show()
  120. state = 1
  121. def game_waiting():
  122. global state,score,flappy_bird,obstacle_1,obstacle_2, pressed
  123. if clicked():
  124. flappy_bird = FlappyBird()
  125. obstacle_1 = Obstacle(WIDTH)
  126. obstacle_2 = Obstacle(WIDTH + (WIDTH + colu_size[0]) // 2)
  127. state = 2
  128. def game_running():
  129. global state
  130. if clicked(): flappy_bird.flap()
  131. flappy_bird.move()
  132. if flappy_bird.crashed():
  133. flappy_bird.y = HEIGHT - flappy_bird.height
  134. state = 3
  135. obstacle_1.scroll()
  136. obstacle_2.scroll()
  137. if obstacle_1.collided(flappy_bird.y) or obstacle_2.collided(flappy_bird.y):
  138. state = 3
  139. draw()
  140. def game_over():
  141. global state, high_score
  142. oled.fill_rect(WIDTH//2-32, 10, 64, 23, 0)
  143. oled.rect(WIDTH//2-32, 10, 64, 23, 1)
  144. oled.text('G A M E', WIDTH//2-28, 13)
  145. oled.text('O V E R', WIDTH//2-28, 23)
  146. score = obstacle_1.score + obstacle_2.score
  147. if score > high_score:
  148. high_score = score
  149. oled.fill_rect(WIDTH//2-48, 37, 96, 14, 0)
  150. oled.rect(WIDTH//2-48, 37, 96, 14, 1)
  151. oled.text('New record!',WIDTH//2-44, 40)
  152. write_high_score(high_score)
  153. oled.show()
  154. try:
  155. send_score(score)
  156. except:
  157. pass
  158. state = 1
  159. def loop():
  160. while True:
  161. if state == 0: splash_screen()
  162. elif state == 1: game_waiting()
  163. elif state == 2: game_running()
  164. elif state == 3: game_over()
  165. loop()

oled驱动,ssd1306.py

  1. # MicroPython SSD1306 OLED driver, I2C and SPI interfaces
  2. from micropython import const
  3. import framebuf
  4. # register definitions
  5. SET_CONTRAST = const(0x81)
  6. SET_ENTIRE_ON = const(0xA4)
  7. SET_NORM_INV = const(0xA6)
  8. SET_DISP = const(0xAE)
  9. SET_MEM_ADDR = const(0x20)
  10. SET_COL_ADDR = const(0x21)
  11. SET_PAGE_ADDR = const(0x22)
  12. SET_DISP_START_LINE = const(0x40)
  13. SET_SEG_REMAP = const(0xA0)
  14. SET_MUX_RATIO = const(0xA8)
  15. SET_COM_OUT_DIR = const(0xC0)
  16. SET_DISP_OFFSET = const(0xD3)
  17. SET_COM_PIN_CFG = const(0xDA)
  18. SET_DISP_CLK_DIV = const(0xD5)
  19. SET_PRECHARGE = const(0xD9)
  20. SET_VCOM_DESEL = const(0xDB)
  21. SET_CHARGE_PUMP = const(0x8D)
  22. # Subclassing FrameBuffer provides support for graphics primitives
  23. # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
  24. class SSD1306(framebuf.FrameBuffer):
  25. def __init__(self, width, height, external_vcc):
  26. self.width = width
  27. self.height = height
  28. self.external_vcc = external_vcc
  29. self.pages = self.height // 8
  30. self.buffer = bytearray(self.pages * self.width)
  31. super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
  32. self.init_display()
  33. def init_display(self):
  34. for cmd in (
  35. SET_DISP | 0x00, # off
  36. # address setting
  37. SET_MEM_ADDR,
  38. 0x00, # horizontal
  39. # resolution and layout
  40. SET_DISP_START_LINE | 0x00,
  41. SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
  42. SET_MUX_RATIO,
  43. self.height - 1,
  44. SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
  45. SET_DISP_OFFSET,
  46. 0x00,
  47. SET_COM_PIN_CFG,
  48. 0x02 if self.width > 2 * self.height else 0x12,
  49. # timing and driving scheme
  50. SET_DISP_CLK_DIV,
  51. 0x80,
  52. SET_PRECHARGE,
  53. 0x22 if self.external_vcc else 0xF1,
  54. SET_VCOM_DESEL,
  55. 0x30, # 0.83*Vcc
  56. # display
  57. SET_CONTRAST,
  58. 0xFF, # maximum
  59. SET_ENTIRE_ON, # output follows RAM contents
  60. SET_NORM_INV, # not inverted
  61. # charge pump
  62. SET_CHARGE_PUMP,
  63. 0x10 if self.external_vcc else 0x14,
  64. SET_DISP | 0x01,
  65. ): # on
  66. self.write_cmd(cmd)
  67. self.fill(0)
  68. self.show()
  69. def poweroff(self):
  70. self.write_cmd(SET_DISP | 0x00)
  71. def poweron(self):
  72. self.write_cmd(SET_DISP | 0x01)
  73. def contrast(self, contrast):
  74. self.write_cmd(SET_CONTRAST)
  75. self.write_cmd(contrast)
  76. def invert(self, invert):
  77. self.write_cmd(SET_NORM_INV | (invert & 1))
  78. def show(self):
  79. x0 = 0
  80. x1 = self.width - 1
  81. if self.width == 64:
  82. # displays with width of 64 pixels are shifted by 32
  83. x0 += 32
  84. x1 += 32
  85. self.write_cmd(SET_COL_ADDR)
  86. self.write_cmd(x0)
  87. self.write_cmd(x1)
  88. self.write_cmd(SET_PAGE_ADDR)
  89. self.write_cmd(0)
  90. self.write_cmd(self.pages - 1)
  91. self.write_data(self.buffer)
  92. class SSD1306_I2C(SSD1306):
  93. def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
  94. self.i2c = i2c
  95. self.addr = addr
  96. self.temp = bytearray(2)
  97. self.write_list = [b"\x40", None] # Co=0, D/C#=1
  98. super().__init__(width, height, external_vcc)
  99. def write_cmd(self, cmd):
  100. self.temp[0] = 0x80 # Co=1, D/C#=0
  101. self.temp[1] = cmd
  102. self.i2c.writeto(self.addr, self.temp)
  103. def write_data(self, buf):
  104. self.write_list[1] = buf
  105. self.i2c.writevto(self.addr, self.write_list)
  106. class SSD1306_SPI(SSD1306):
  107. def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
  108. self.rate = 10 * 1024 * 1024
  109. dc.init(dc.OUT, value=0)
  110. res.init(res.OUT, value=0)
  111. cs.init(cs.OUT, value=1)
  112. self.spi = spi
  113. self.dc = dc
  114. self.res = res
  115. self.cs = cs
  116. import time
  117. self.res(1)
  118. time.sleep_ms(1)
  119. self.res(0)
  120. time.sleep_ms(10)
  121. self.res(1)
  122. super().__init__(width, height, external_vcc)
  123. def write_cmd(self, cmd):
  124. self.spi.init(baudrate=self.rate, polarity=0, phase=0)
  125. self.cs(1)
  126. self.dc(0)
  127. self.cs(0)
  128. self.spi.write(bytearray([cmd]))
  129. self.cs(1)
  130. def write_data(self, buf):
  131. self.spi.init(baudrate=self.rate, polarity=0, phase=0)
  132. self.cs(1)
  133. self.dc(1)
  134. self.cs(0)
  135. self.spi.write(buf)
  136. self.cs(1)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/400655
推荐阅读
相关标签
  

闽ICP备14008679号