「图像处理」Python-OpenCV图像生成、叠加、响应鼠标事件&Python数组随机取元素&Numpy的简单应用

标题有点长

其实标题是:《使用Opencv-Python实现2048小游戏

但这么写谁看呢?哈哈哈哈

不过当然是涉及到了标题所说的一些问题

源码位置:https://github.com/RainkLH/2048GameByOpencv_Python/blob/master/opcv2048.py


目录

1 Opencv-Python中生成图片

1.1 游戏背景图

1.2 数字块的图

2 Opencv-Python响应鼠标事件(游戏的滑动操作)

第一步,注册鼠标事件函数:

第二步,实现鼠标响应函数:

3 Python数组随机取元素(每滑动一次随机产生新的数字块)

4 (上下左右的滑动处理)

5 (其他函数和调用)


本文代码为Python语言,需要如下模块

import cv2
import numpy as np
import random

接着,要实现2048小游戏,需要一些全局变量

# 用于随机产生新元素 为了方便调整概率以列表形式创建
new_nums = [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3]
new_vals = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4]
# 4x4的游戏数据
data = np.array([[0, 0, 0, 0],
                 [0, 0, 0, 0],
                 [0, 0, 0, 0],
                 [0, 0, 0, 0]])
# 用于记录鼠标滑动的数据: 开始(按下) 和结束(抬起)
m_start = [0, 0]
m_end = [0, 0]

然后是各功能代码

1 Opencv-Python中生成图片

1.1 游戏背景图

在python中,opencv没有“Mat”数据类型,而是直接使用了Numpy中的数组,原本Mat的操作全部变成np数组的操作了。

比如创建一张白色的图像(作为2048小游戏的基础背景图):

# 创建背景图
def create_back():
    color = (255, 255, 255)
    img = np.array([[color for i in range(450)]for j in range(450)], dtype=np.uint8)
    return img

就是一张450x450的白色图像,不展示了

1.2 数字块的图

# 创建色块
def create_block(num):
    # 获取数字位数
    index = len(str(num))
    # 16个格最大数字不会超过2的16次方 也就是65536,也就是5位数,
    # 根据数字长度来确定块的颜色、文字大小、粗细、位置
    greens = [180, 162, 144, 126, 108]
    font_sizes = [2.75, 1.75, 1.25, 1, 0.875]
    thickness_s = [6, 5, 4, 3, 2]
    color = (54, greens[index - 1], 60)
    font_size = font_sizes[index-1]
    thickness = thickness_s[index - 1]
    pos = (int(24 - (3.3 * index)), int(85 - (6.5 * index)))
    # 创建数字块
    img = np.array([[color for i in range(100)] for j in range(100)], dtype=np.uint8)
    if num > 0:
        #  大于0的数字进行绘制
        cv2.putText(img, str(num), pos, cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 255, 255), thickness)
    return img

把2、4、8、16……65536一共16个数字的块全部show出来就是这样,随着数字增大,块的颜色加深。

2 Opencv-Python响应鼠标事件(游戏的滑动操作)

Opencv-Python中支持的鼠标事件有以下这些。

事件 编号 注释
CV_EVENT_MOUSEMOVE 0 滑动
EVENT_LBUTTONDOWN 1 左键点击
EVENT_RBUTTONDOWN 2 右键点击
EVENT_MBUTTONDOWN 3 中间点击
EVENT_LBUTTONUP 4 左键释放
EVENT_RBUTTONUP 5 右键释放
EVENT_MBUTTONUP 6 中间释放
EVENT_LBUTTONDBLCLK 7 左键双击
EVENT_RBUTTONDBLCLK 8 右键双击
EVENT_MBUTTONDBLCLK 9 中间双击

这里主要用到 鼠标左键的按下和抬起

第一步,注册鼠标事件函数:

在游戏初始化,启动的时候,定义窗体名称,绑定鼠标事件函数,然后生成最开始的数字块,并显示画面

# 初始化游戏界面
def game_run():
    cv2.namedWindow("2048", cv2.WINDOW_AUTOSIZE)
    # 绑定鼠标响应函数
    cv2.setMouseCallback("2048", mouse_event)
    new_elements()
    img = game_image()
    cv2.imshow("2048", img)
    cv2.waitKey(0)

第二步,实现鼠标响应函数:

左键按下记录坐标,左键抬起记录坐标,然后计算前后坐标判断是向哪个方向的滑动,执行相应函数(即移动数字块,相邻且相同的求和),接着再生成新的数字块,跟新界面。

这里有个很不好处理的问题,opencv里,绑定了鼠标事件函数后,只要鼠标再窗口内,就会触发,不论你是移动、双击、点击……函数写不好容易导致“只要鼠标一进入窗口,就会疯狂执行一些操作的问题”。

# 鼠标事件
def mouse_event(event, x, y, flags, param):
    global m_start, m_end
    if event == cv2.EVENT_LBUTTONDOWN:
        m_start = [x, y]
    if event == cv2.EVENT_LBUTTONUP:
        m_end = [x, y]
        dx = m_end[0] - m_start[0]
        dy = m_end[1] - m_start[1]
        if abs(dx) > abs(dy):
            if dx > 0:
                to_right()
            else:
                to_left()
        else:
            if dy > 0:
                to_bottom()
            else:
                to_top()
        img = game_image()
        cv2.imshow("2048", img)
        cv2.waitKey(100)  # 滑动后更新画面,停顿一下再插入新的数字使有动画的感觉
        new_elements()
        img = game_image()
        cv2.imshow("2048", img)
        m_start = [0, 0]
        m_end = [0, 0]


3 Python数组随机取元素(每滑动一次随机产生新的数字块)

这里主要用到了random模块里面的

random.choice()---->从列表中随机取一个元素

random.sample()------>从列表中随机取指定个数个元素

# 产生新元素
def new_elements():
    pos_list_t = np.where(data == 0)
    pos_list = [(pos_list_t[0][i], pos_list_t[1][i]) for i in range(len(pos_list_t[0]))]
    # 随机一个新元素的个数
    nums = random.choice(new_nums)
    if nums >= len(pos_list):
        nums = 1
    # 随机一个新元素的位置
    new_poss = random.sample(pos_list, nums)
    for pos in new_poss:
        # 随机一个新元素的数值
        data[pos[0]][pos[1]] = random.choice(new_vals)

4 (上下左右的滑动处理)

前三个就是设计的技术核心,接下来就是算法核心,向一个方向滑动,就是4x4的矩阵中,非零数字向指定方向移动,然后把相邻且相同的数字求和变成一个数字。

第一行向左滑动一次举个例子,

[1, 0, 1, 2]-->[1, 1, 2, 0]-->[2, 0, 2, 0]-->[2, 2, 0, 0]

代码如下(如果你们有更简单快速的方法,务必留言教教我)

# 向左滑动
def to_left():
    for i in range(4):
        index = 0
        t = [0, 0, 0, 0]
        for x in range(3):
            if data[i][x] > 0:
                t[index] = data[i][x]
                data[i][x] = 0
                n = x + 1
                while data[i][n] == 0 and n < 3:
                    n = n + 1
                if t[index] == data[i][n]:
                    t[index], data[i][n] = t[index] + data[i][n], 0
                    x += 1
                index += 1
        t[index] = data[i][3]
        data[i] = t


# 向右
def to_right():
    for i in range(4):
        index = 3
        t = [0, 0, 0, 0]
        for x in range(3, 0, -1):
            if data[i][x] > 0:
                t[index] = data[i][x]
                data[i][x] = 0
                n = x - 1
                while data[i][n] == 0 and n >= 0:
                    n = n - 1
                if t[index] == data[i][n]:
                    t[index], data[i][n] = t[index] + data[i][n], 0
                    x -= 1
                index -= 1
        t[index] = data[i][0]
        data[i] = t


# 向上
def to_top():
    for i in range(4):
        index = 0
        t = [0, 0, 0, 0]
        for x in range(3):
            if data[x][i] > 0:
                t[index] = data[x][i]
                data[x][i] = 0
                n = x + 1
                while data[n][i] == 0 and n < 3:
                    n = n + 1
                if t[index] == data[n][i]:
                    t[index], data[n][i] = t[index] + data[n][i], 0
                    x += 1
                index += 1
        t[index] = data[3][i]
        data[:, i] = t


# 向下
def to_bottom():
    for i in range(4):
        index = 3
        t = [0, 0, 0, 0]
        for x in range(3, 0, -1):
            if data[x][i] > 0:
                t[index] = data[x][i]
                data[x][i] = 0
                n = x - 1
                while data[n][i] == 0 and n >= 0:
                    n = n - 1
                if t[index] == data[n][i]:
                    t[index], data[n][i] = t[index] + data[n][i], 0
                    x -= 1
                index -= 1
        t[index] = data[0][i]
        data[:, i] = t

5 (其他函数和调用)

还需要一个根据当前游戏数data[4x4]来生成游戏界面的函数

# 生成当前画面
def game_image():
    img = create_back()
    for i in range(4):
        for j in range(4):
            x_start = 10 + 110 * i
            y_start = 10 + 110 * j
            x_end = x_start + 100
            y_end = y_start + 100
            block = create_block(data[i][j])
            img[x_start:x_end, y_start:y_end] = block
    return img

最后,进行调用启动游戏

if __name__ == "__main__":
    game_run()

 画面如下

尾巴:当然还有很多Bug,这里只是整理一些技术,玩一玩就好,真要写游戏肯定也不能拿Opencv来搞的啊。

发布了21 篇原创文章 · 获赞 5 · 访问量 3663

猜你喜欢

转载自blog.csdn.net/Raink_LH/article/details/104005361