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()