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

news2025/6/21 8:29:17

来自: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其实就明白,所有的操作,以帧为单位

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

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

更新位置之后,调用draw接口,在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"(块传输)的简称,通常用于在内存中进行图像数据的复制或传输操作。

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

可以看出,常规游戏的实现思路,就是基于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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1584903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【uniapp】省市区下拉列表组件

1. 效果图 2. 组件完整代码 <template><view class="custom-area-picker"><view

Day:006(1) | Python爬虫:高效数据抓取的编程技术(爬虫工具)

selenium介绍与安装 Selenium是一个Web的自动化测试工具&#xff0c;最初是为网站自动化测试而开发的&#xff0c;类型像我们玩游戏用的按键精灵&#xff0c;可以按指定的命令自动操作&#xff0c;不同是Selenium 可以直接运行在浏览器上&#xff0c;它支持所有主流的浏览器&am…

idea如何debug看springsecurity的过滤器顺序

idea如何debug看springsecurity的过滤器顺序 先配置一个Spring启动对象,后续需要根据这个对象来获取SpringSecurity的过滤器链 设置一个输出信息&#xff0c;需要在输出信息这里打上断点&#xff0c;才方便查看过滤器链 public static void main(String[] args) {//此时不…

[opencv]VideoWriter写出fourcc格式

fourcc支持的格式 fourcc全名Four-Character Codes&#xff0c;四字符代码&#xff0c;该编码由四个字符组成 cv2.VideoWriter_fourcc(O,O,O,O) cv2.VideoWriter_fourcc(*OOOO) 通常写法有上述两种形式&#xff0c;O代表一个字符&#xff0c;通常有 支持avi格式的有&#…

LeetCode 142.环形链表II(数学公式推导)

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整…

C语言中的数据结构--链表的应用1(2)

前言 上一节我们学习了链表的概念以及链表的实现&#xff0c;那么本节我们就来了解一下链表具体有什么用&#xff0c;可以解决哪些实质性的问题&#xff0c;我们借用习题来加强对链表的理解&#xff0c;那么废话不多说&#xff0c;我们正式进入今天的学习 单链表相关经典算法O…

配置及第三方授权申请教程

项目需要配置的地方不多&#xff0c;主要就两个地方需要注意&#xff1a;邮箱授权和第三方授权需要提前申请 1.基本设置 1.1 打开application.yml&#xff0c;修改数据库ip等基本信息 这些基本的配置就不多说了&#xff0c;基本就是改下服务器ip和账号密码什么的 1.2 获取QQ…

WebGIS实现各地区COVID-19数据一览

1.项目地址 GISpjd/WebGIS-Show-Covid19 (github.com)&#xff0c;具体每个文件的职能可以参考README文档。 2.前言 预览 >> 所用技术栈&#xff1a; 项目需求本身不是过于复杂&#xff0c;所以没有在相应前端框架下完成&#xff0c;但转入框架也是比较容易的 &#…

DJ-D500/0.5机械式隔膜计量泵

一、DJ-D500/0.5机械式隔膜计量泵概述&#xff1a;DJ-D500/0.5机械式隔膜计量泵是一款设计精良、结构紧凑的计量设备&#xff0c;专为精确输送和计量各种化学液体而设计。该泵采用先进的机械驱动机制&#xff0c;能够确保在各种工作压力下都能实现高精度的流量控制。 二、技术特…

Transformer模型-数据预处理,训练,推理(预测)的简明介绍

Transformer模型-数据预处理&#xff0c;训练&#xff0c;推理&#xff08;预测&#xff09;的简明介绍 在继续探讨之前&#xff0c;假定已经对各个模块的功能有了充分的了解&#xff1a; 人工智能AI 虚拟现实VR 黑客帝国_Ankie&#xff08;资深技术项目经理&#xff09;的博客…

Vue2创建过程记录

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、搭建node二、安装Vue CLI三、搭建新项目四、Elemet安装&#xff08;参照官网步骤[Element官网](https://element.eleme.cn/#/zh-CN/component/installation)&am…

2024年安卓轮播图代码+定时翻页(全网代码最少实现)

2024年安卓轮播图代码定时翻页 asda 这里是Fragment子类的继承如果使用 AppCompatActivity请修改一下很简单的如果又看不懂的话可以访问使用我的gpt&#xff1a;https://0.00000.work/ 免费3.5的 直接吧代码扔给他然后和他说帮忙解释一下每一行作用 Integer[] data{R.drawab…

Nexpose v6.6.245 for Linux Windows - 漏洞扫描

Nexpose v6.6.245 for Linux & Windows - 漏洞扫描 Rapid7 Vulnerability Management, Release Apr 03, 2024 请访问原文链接&#xff1a;Nexpose v6.6.245 for Linux & Windows - 漏洞扫描&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&…

JVM字节码与类的加载——class文件结构

文章目录 1、概述1.1、class文件的跨平台性1.2、编译器分类1.3、透过字节码指令看代码细节 2、虚拟机的基石&#xff1a;class文件2.1、字节码指令2.2、解读字节码方式 3、class文件结构3.1、魔数&#xff1a;class文件的标识3.2、class文件版本号3.3、常量池&#xff1a;存放所…

Vue笔记 2

数据代理 数据代理&#xff1a;通过一个对象代理对另一个对象中属性的操作&#xff08;读/写&#xff09; let obj{x:100} let obj2{y:200} Object.defineProperty(obj2,x,{get(){return obj.x},set(value){obj.x value} })Vue中的数据代理 Vue中的数据代理&#xff1a; 通…

cesium 添加动态波纹效果 圆形扩散效果 波纹材质

一、扩展材质 /*** 水波纹扩散材质* param {*} options* param {String} options.color 颜色* param {Number} options.duration 持续时间 毫秒* param {Number} options.count 波浪数量* param {Number} options.gradient 渐变曲率*/function CircleWaveMaterialProperty(opt…

顶顶通呼叫中心中间件(mod_cti基于FreeSWITCH)-回铃音补偿

文章目录 前言联系我们解决问题操作步骤 前言 回铃音&#xff1a; 当别人打电话给你时&#xff0c;你的电话响铃了&#xff0c;而他听到的声音叫做回铃音。回铃音是被叫方向主叫方传送&#xff0c;也是彩铃功能的基础。我们平时打电话听到的“嘟 嘟 嘟 嘟”的声音&#xff0c;就…

Golang | Leetcode Golang题解之第20题有效的括号

题目&#xff1a; 题解&#xff1a; func isValid(s string) bool {n : len(s)if n % 2 1 {return false}pairs : map[byte]byte{): (,]: [,}: {,}stack : []byte{}for i : 0; i < n; i {if pairs[s[i]] > 0 {if len(stack) 0 || stack[len(stack)-1] ! pairs[s[i]] {…

白盒测试-条件覆盖

​ 条件覆盖是指运行代码进行测试时&#xff0c;程序中所有判断语句中的条件取值为真值为假的情况都被覆盖到&#xff0c;即每个判断语句的所有条件取真值和假值的情况都至少被经历过一次。 ​ 条件覆盖率的计算方法为&#xff1a;测试时覆盖到的条件语句真、假情况的总数 / 程…

期货学习笔记-MACD指标学习2

MACD底背离把握买入多单的技巧 底背离的概念及特征 底背离指的是MACD指标与价格低点之间的对比关系&#xff0c;这里需要明白的是MACD指标的涨跌动能和价格形态衰竭形态之间的关系&#xff0c;如果市场价格创新低而出现衰竭形态同时也有底背离形态的出现&#xff0c;此时下跌…