MediaPipe+OpenCV的python实现交互式贪吃蛇小游戏

1,安装依赖

pip install opencv-python mediapipe numpy

2,代码

python 复制代码
import cv2
import numpy as np
import mediapipe as mp
import random
import math


mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(
    static_image_mode=False,
    max_num_hands=1,
    min_detection_confidence=0.7,
    min_tracking_confidence=0.7
)


WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600


WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (0, 0, 255)
BLUE = (255, 0, 0)
YELLOW = (0, 255, 255)
PURPLE = (255, 0, 255)
ORANGE = (0, 165, 255)


class TrailSnakeGame:
    def __init__(self):
        self.reset()

    def reset(self):
        
        self.trail = []
        self.trail_length = 30  
        self.score = 0
        self.food_radius = 25  
        self.food = self.generate_food()  
        self.game_over = False
        self.snake_thickness = 15
        self.growth_amount = 10  
        self.min_trail_length_for_collision = 25  

        
        self.smoothing_factor = 0.7  
        self.last_smoothed_pos = None

    def generate_food(self):
        food = {
            'pos': (random.randint(60, WINDOW_WIDTH - 60),
                    random.randint(60, WINDOW_HEIGHT - 60)),
            'radius': self.food_radius,  
            'color': RED,
            'points': 1
        }
        return food

    def smooth_position(self, current_pos):
        """平滑处理位置坐标,减少抖动"""
        if self.last_smoothed_pos is None:
            self.last_smoothed_pos = current_pos
            return current_pos

        
        smoothed_x = int(self.smoothing_factor * current_pos[0] +
                         (1 - self.smoothing_factor) * self.last_smoothed_pos[0])
        smoothed_y = int(self.smoothing_factor * current_pos[1] +
                         (1 - self.smoothing_factor) * self.last_smoothed_pos[1])

        smoothed_pos = (smoothed_x, smoothed_y)
        self.last_smoothed_pos = smoothed_pos
        return smoothed_pos

    def update_trail(self, finger_pos):
        if self.game_over:
            return

        if finger_pos is None:
            return

        
        smoothed_pos = self.smooth_position(finger_pos)

        
        if len(self.trail) > 0:
            last_pos = self.trail[-1]
            distance = math.sqrt((smoothed_pos[0] - last_pos[0]) ** 2 +
                                 (smoothed_pos[1] - last_pos[1]) ** 2)
            
            if distance < 3:  
                return

        
        self.trail.append(smoothed_pos)

        
        if len(self.trail) > self.trail_length:
            self.trail.pop(0)

        
        self.check_food_collision(smoothed_pos)

        
        if len(self.trail) > self.min_trail_length_for_collision:
            self.check_self_collision()

    def check_food_collision(self, head_pos):
        if len(self.trail) == 0:
            return

        food_x, food_y = self.food['pos']

        
        distance = math.sqrt((head_pos[0] - food_x) ** 2 + (head_pos[1] - food_y) ** 2)

        
        collision_distance = self.snake_thickness + self.food['radius'] - 8
        if distance < collision_distance:
            self.score += self.food['points']
            self.trail_length += self.growth_amount
            self.food = self.generate_food()
            print(f"吃到食物!+{self.food['points']}分,当前分数: {self.score}, 拖尾长度: {self.trail_length}")

    def check_self_collision(self):
        if len(self.trail) < self.min_trail_length_for_collision:
            return

        head_pos = self.trail[-1]

        
        
        skip_points = max(15, int(len(self.trail) * 0.4))  

        collision_detected = False
        for i in range(len(self.trail) - skip_points):
            body_pos = self.trail[i]
            distance = math.sqrt((head_pos[0] - body_pos[0]) ** 2 +
                                 (head_pos[1] - body_pos[1]) ** 2)

            
            collision_threshold = self.snake_thickness - 5
            if distance < collision_threshold:
                collision_detected = True
                break

        if collision_detected:
            self.game_over = True
            print(f"游戏结束!撞到自己了,当前分数: {self.score}")

    def draw(self, frame, finger_pos):
        
        game_frame = np.zeros((WINDOW_HEIGHT, WINDOW_WIDTH, 4), dtype=np.uint8)

        
        game_frame[:, :, 3] = 0

        
        if len(self.trail) > 1:
            for i in range(1, len(self.trail)):
                
                color_ratio = i / len(self.trail)
                r = int(255 * (1 - color_ratio))
                g = int(255 * color_ratio)
                b = int(128 + 127 * math.sin(color_ratio * math.pi))
                color = (b, g, r, 255)  

                
                cv2.line(game_frame, self.trail[i - 1], self.trail[i],
                         color, self.snake_thickness, lineType=cv2.LINE_AA)

        
        if len(self.trail) > 0:
            head_pos = self.trail[-1]
            
            cv2.circle(game_frame, head_pos, self.snake_thickness, (0, 255, 0, 255), -1, lineType=cv2.LINE_AA)
            cv2.circle(game_frame, head_pos, self.snake_thickness // 2, (255, 255, 0, 255), 2, lineType=cv2.LINE_AA)

        
        food_pos = self.food['pos']
        
        cv2.circle(game_frame, food_pos, self.food['radius'], (0, 0, 255, 255), -1, lineType=cv2.LINE_AA)
        
        highlight_pos = (food_pos[0] - self.food['radius'] // 3, food_pos[1] - self.food['radius'] // 3)
        cv2.circle(game_frame, highlight_pos, self.food['radius'] // 4, (255, 255, 255, 200), -1, lineType=cv2.LINE_AA)
        
        cv2.circle(game_frame, food_pos, self.food['radius'] + 2, (255, 255, 255, 255), 2, lineType=cv2.LINE_AA)

        
        game_rgb = game_frame[:, :, :3]
        game_alpha = game_frame[:, :, 3] / 255.0

        
        frame_height, frame_width = frame.shape[:2]
        game_x_start = frame_width - WINDOW_WIDTH
        game_y_start = 0

        
        if (game_x_start >= 0 and game_y_start >= 0 and
                game_x_start + WINDOW_WIDTH <= frame_width and
                game_y_start + WINDOW_HEIGHT <= frame_height):

            
            game_region = frame[game_y_start:game_y_start + WINDOW_HEIGHT,
                          game_x_start:game_x_start + WINDOW_WIDTH]

            
            for c in range(3):
                game_region[:, :, c] = (game_rgb[:, :, c] * game_alpha +
                                        game_region[:, :, c] * (1 - game_alpha))

        
        cv2.putText(frame, f"Score: {self.score}", (frame_width - WINDOW_WIDTH + 20, 40),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, WHITE, 2)

        cv2.putText(frame, f"Trail Length: {len(self.trail)}",
                    (frame_width - WINDOW_WIDTH + 20, 80),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, WHITE, 2)

        
        cv2.putText(frame, "Move your finger to control the snake",
                    (frame_width - WINDOW_WIDTH + 20, WINDOW_HEIGHT - 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.6, WHITE, 1)

        
        if self.game_over:
            
            overlay = frame.copy()
            cv2.rectangle(overlay,
                          (frame_width - WINDOW_WIDTH, 0),
                          (frame_width, WINDOW_HEIGHT),
                          (0, 0, 0, 128), -1)
            cv2.addWeighted(overlay, 0.5, frame, 0.5, 0, frame)

            
            cv2.putText(frame, "GAME OVER",
                        (frame_width - WINDOW_WIDTH // 2 - 100, WINDOW_HEIGHT // 2 - 30),
                        cv2.FONT_HERSHEY_SIMPLEX, 1.5, RED, 3)
            cv2.putText(frame, f"Final Score: {self.score}",
                        (frame_width - WINDOW_WIDTH // 2 - 80, WINDOW_HEIGHT // 2 + 20),
                        cv2.FONT_HERSHEY_SIMPLEX, 1, WHITE, 2)
            cv2.putText(frame, "Press 'R' to Restart",
                        (frame_width - WINDOW_WIDTH // 2 - 80, WINDOW_HEIGHT // 2 + 60),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.7, WHITE, 2)

        return frame


def get_finger_position(hand_landmarks, frame_shape):
    """获取食指指尖位置"""
    index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
    h, w = frame_shape[:2]
    finger_x = int(index_tip.x * w)
    finger_y = int(index_tip.y * h)
    return (finger_x, finger_y)


def main():
    cap = cv2.VideoCapture(0)
    game = TrailSnakeGame()

    
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

    print("拖尾贪吃蛇游戏开始!")
    print("移动手指来控制蛇的移动")
    print("触碰红色大圆圈来获得分数并变长")
    print("避免让蛇头撞到自己的身体")
    print("按R重新开始游戏,按Q退出")

    while True:
        ret, frame = cap.read()
        if not ret:
            print("无法读取摄像头画面")
            break

        
        frame = cv2.flip(frame, 1)

        
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = hands.process(rgb_frame)

        finger_pos = None
        game_finger_pos = None

        
        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                
                mp_drawing.draw_landmarks(
                    frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

                
                finger_pos = get_finger_position(hand_landmarks, frame.shape)

                
                if finger_pos:
                    cv2.circle(frame, finger_pos, 12, (0, 255, 0), -1, lineType=cv2.LINE_AA)
                    cv2.circle(frame, finger_pos, 8, (255, 255, 255), 2, lineType=cv2.LINE_AA)

        
        if finger_pos:
            frame_height, frame_width = frame.shape[:2]
            game_x_start = frame_width - WINDOW_WIDTH
            game_y_start = 0

            
            if (game_x_start <= finger_pos[0] < frame_width and
                    game_y_start <= finger_pos[1] < WINDOW_HEIGHT):
                
                game_finger_x = finger_pos[0] - game_x_start
                game_finger_y = finger_pos[1] - game_y_start
                game_finger_pos = (game_finger_x, game_finger_y)

        
        game.update_trail(game_finger_pos)

        
        frame = game.draw(frame, game_finger_pos)

        
        cv2.imshow('Smooth Trail Snake Game', frame)

        
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q'):
            break
        elif key == ord('r'):
            game.reset()
            print("游戏重新开始!")

    cap.release()
    cv2.destroyAllWindows()
    print("游戏结束!")


if __name__ == "__main__":
    main()
相关推荐
汽车仪器仪表相关领域3 小时前
汽车排放检测的 “模块化核心”:HORIBA OBS-ONE GS Unit 气体分析单元技术解析
大数据·人工智能·功能测试·车载系统·汽车·安全性测试·汽车检测
盼哥PyAI实验室4 小时前
Python 正则表达式实战 + 详解:从匹配QQ邮箱到掌握核心语法
python·mysql·正则表达式
恒点虚拟仿真4 小时前
“AI+XR”赋能智慧研创中心:告别AI焦虑,重塑教师未来
人工智能·xr·虚拟仿真·虚拟仿真教学·xr研创中心·数字教师·未来教师
木易 士心4 小时前
Android 开发核心技术深度解析
android·开发语言·python
nju_spy4 小时前
力扣每日一题(四)线段树 + 树状数组 + 差分
数据结构·python·算法·leetcode·面试·线段树·笔试
2501_938931254 小时前
解构AI营销获客工具的四大智能中枢与价值逻辑
人工智能·机器学习·自动驾驶
Liquad Li4 小时前
汽车配件 AI 系统:重构汽车配件管理与多语言内容生成新范式
人工智能
小白狮ww4 小时前
VASP 教程:使用 VASP 进行机器学习力场训练
人工智能·深度学习·机器学习·大模型·分子动力学·计算机程序·vasp
ayingmeizi1634 小时前
重构增长:生成式AI如何将CRM打造为企业的销售大脑
人工智能·重构