一、简介
在传统的游戏控制中,玩家通常依赖键盘、鼠标或者游戏手柄来进行操作。然而,随着计算机视觉技术的发展,越来越多的研究者和开发者开始探索通过手势和动作来操控游戏的可能性。本文介绍的游戏控制插件就是基于这个理念开发的,允许玩家通过手势和头部动作控制游戏中的角色移动、跳跃、攻击等操作,为游戏提供了一种更加自然和沉浸式的控制方式。
二、功能介绍
本项目的主要功能包括:
- 手势控制移动:通过抬手、举手等动作,实现游戏中的前后左右移动。
- 跳跃检测:玩家只需快速向上跳跃,系统将自动检测并在游戏中触发跳跃动作。
- 攻击控制:双手平行向前推,触发游戏中的攻击操作。
- 头部控制视角:通过头部的上下左右移动,模拟鼠标的视角控制。
- 自定义按键映射 通过用户界面(UI),用户可以自由配置各个手势和头部动作对应的游戏按键。
三、技术实现
1. Mediapipe 与 OpenCV 实现手势检测
本项目采用了Mediapipe库进行实时的人体姿态识别。Mediapipe 是由 Google 开发的一个强大开源框架,能够高效地检测和跟踪人体的 33 个关键点(如手腕、肘部、肩膀等)。结合OpenCV,我们可以实时捕捉用户的动作,并在视频流中进行关键点标注和检测。
Mediapipe 数据点映射,Mediapipe 全身动作捕获可以产生一个.csv 包括33个关键点的[x,y,z] 坐标.
import cv2
import mediapipe as mp
class PoseDetector:
def __init__(self):
self.pose = mp.solutions.pose.Pose()
self.mp_drawing = mp.solutions.drawing_utils
def detect_pose(self, frame):
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.pose.process(image_rgb)
return results
def draw_landmarks(self, frame, landmarks):
self.mp_drawing.draw_landmarks(
frame, landmarks, mp.solutions.pose.POSE_CONNECTIONS)
2. Pynput 实现键盘和鼠标模拟
为了将手势和头部动作映射到游戏控制按键上,本项目使用了Pynput库模拟键盘和鼠标的输入。通过简单的键盘映射,玩家可以将指定手势对应到游戏中的不同按键(如“W”、“A”、“S”、“D”)。
from pynput.keyboard import Controller, Key
class InputSimulator:
def __init__(self):
self.keyboard = Controller()
def press_key(self, key):
self.keyboard.press(key)
def release_key(self, key):
self.keyboard.release(key)
四、可运行片段
1. 环境准备
在开始之前,请确保你已经安装了以下依赖项 (在使用mediapipe时可能会出现tensorflow 版本问题,注意python版本,mediapipe版本和tensorflow的版本):
- Python 3.7 及以上版本
- 计算机连接有相机
- Mediapipe
- OpenCV
- Pynput
- Psutil
- Tkinter
pip install -r requirements.txt
requirements.txt 如下所述
Mediapipe
OpenCV
Pynput
Psutil
Tkinter
2. 完整的代码你可以跑一跑
以下代码可以用来控制网页小游戏,你不一定要给出游戏的运行路径。
注意:有一些游戏是反这种插件的,可能会存在打不开的问题,你需要完整的插件请到我的GitHub参阅。
import subprocess
import time
import cv2
import mediapipe as mp
import pyautogui
def open_game():
# 游戏路径
game_path = '你的游戏路径'
try:
# 用subprocess打开游戏
print(f"Launching the game from: {game_path}")
subprocess.Popen([game_path])
except Exception as e:
print(f"Error occurred while trying to open the game: {e}")
# 初始化 Mediapipe 进行全身姿势检测
mp_pose = mp.solutions.pose
pose = mp_pose.Pose()
mp_drawing = mp.solutions.drawing_utils
# 控制系统阈值 (依据你的需求调整)
SHOULDER_MARGIN = 0.05 # 肩膀高度5%阈值 (5% tolerance)
BACKWARD_THRESHOLD = 0.1 # 向后运动更大的阈值 (10% tolerance)
JUMP_MARGIN = 0.02 # 跳跃阈值小 (2%)
# 用于存储先前的手腕和肩部位置以进行跳跃检测的变量
prev_left_wrist_y = None
prev_right_wrist_y = None
prev_left_shoulder_y = None
prev_right_shoulder_y = None
def detect_and_control():
global prev_left_wrist_y, prev_right_wrist_y, prev_left_shoulder_y, prev_right_shoulder_y
cap = cv2.VideoCapture(0) # 打开相机
while cap.isOpened():
success, image = cap.read()
if not success:
continue
# 翻转图像以创建镜像效果
image = cv2.flip(image, 1)
# 将图像转换为 Mediapipe 的 RGB
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = pose.process(image_rgb)
if results.pose_landmarks:
# Draw pose landmarks on the video frame
mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
# 获取手腕和肩部的标志
left_wrist = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST] # Swap for mirror effect
right_wrist = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST] # Swap for mirror effect
left_shoulder = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER] # Swap for mirror effect
right_shoulder = results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_SHOULDER] # Swap for mirror effect
# 检测跳跃、前进、左转、右转和后退的运动阈值
both_wrists_at_shoulder = (left_shoulder.y - SHOULDER_MARGIN) < left_wrist.y < (left_shoulder.y + SHOULDER_MARGIN) and \
(right_shoulder.y - SHOULDER_MARGIN) < right_wrist.y < (right_shoulder.y + SHOULDER_MARGIN)
both_wrists_above_shoulder = left_wrist.y < left_shoulder.y and right_wrist.y < right_shoulder.y
left_wrist_above = left_wrist.y < left_shoulder.y and right_wrist.y >= right_shoulder.y + SHOULDER_MARGIN
right_wrist_above = right_wrist.y < right_shoulder.y and left_wrist.y >= left_shoulder.y + SHOULDER_MARGIN
both_wrists_below_shoulder = left_wrist.y > left_shoulder.y + BACKWARD_THRESHOLD and right_wrist.y > right_shoulder.y + BACKWARD_THRESHOLD
# 通过检查手腕和肩膀是否略微向上移动来检测跳跃
if prev_left_wrist_y is not None and prev_right_wrist_y is not None and \
prev_left_shoulder_y is not None and prev_right_shoulder_y is not None:
jump_detected = (left_wrist.y < prev_left_wrist_y - JUMP_MARGIN and
right_wrist.y < prev_right_wrist_y - JUMP_MARGIN and
left_shoulder.y < prev_left_shoulder_y - JUMP_MARGIN and
right_shoulder.y < prev_right_shoulder_y - JUMP_MARGIN)
else:
jump_detected = False
# 基于检测到的姿势的控制逻辑
if both_wrists_above_shoulder:
pyautogui.keyDown('w') # 按 'W' 向前走
print("Moving Forward (W)")
else:
pyautogui.keyUp('w') # 释放 'W'
if left_wrist_above:
pyautogui.keyDown('a') # 按 'A' 向左走
print("Moving Left (A)")
else:
pyautogui.keyUp('a') # 释放 'A'
if right_wrist_above:
pyautogui.keyDown('d') # 按 'D' 向右走
print("Moving Right (D)")
else:
pyautogui.keyUp('d') # 释放 'D'
if both_wrists_below_shoulder:
pyautogui.keyDown('s') # 按 'S' 向后走
print("Moving Backward (S)")
else:
pyautogui.keyUp('s') # 释放 'S'
if both_wrists_at_shoulder:
print("Holding position")
if jump_detected:
pyautogui.press('space') # 跳 (Space)
print("Jumping (Space)")
# 存储当前手腕和肩部位置,以便在下一帧中进行跳跃检测
prev_left_wrist_y = left_wrist.y
prev_right_wrist_y = right_wrist.y
prev_left_shoulder_y = left_shoulder.y
prev_right_shoulder_y = right_shoulder.y
# 显示视频
cv2.imshow('Full-Body Pose Detection', image)
# 按 ESC 退出
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows() # Close OpenCV windows
if __name__ == '__main__':
detect_and_control()
五、总结与展望
本项目展示了通过计算机视觉与游戏控制相结合的可能性。通过Mediapipe与Pynput的结合,玩家可以通过身体动作直接控制游戏中的角色,为游戏带来更为直观和沉浸式的体验。
接下来可以做的优化包括:
- 动作识别优化: 通过使用滤波器(如卡尔曼滤波器)来减少姿态识别中的抖动。
- 支持更多游戏: 针对不同的游戏设计更加丰富的手势动作。
- 手势学习系统: 让用户自定义更多复杂的手势,并将其绑定到不同的按键操作上。
希望这篇博客能为大家提供一些启发,欢迎大家在评论区交流讨论!🎮👾
作者信息:
- 唐嵩翔(Master of Information Systems, 墨尔本大学)
- 沈昕(Master of Information Technology, 墨尔本大学)
- 陈俊豪(Master of Information Systems, 墨尔本大学)