基于mediapipe动作捕捉的控制游戏插件

一、简介

在传统的游戏控制中,玩家通常依赖键盘、鼠标或者游戏手柄来进行操作。然而,随着计算机视觉技术的发展,越来越多的研究者和开发者开始探索通过手势和动作来操控游戏的可能性。本文介绍的游戏控制插件就是基于这个理念开发的,允许玩家通过手势和头部动作控制游戏中的角色移动、跳跃、攻击等操作,为游戏提供了一种更加自然和沉浸式的控制方式。

二、功能介绍

本项目的主要功能包括:

  • 手势控制移动:通过抬手、举手等动作,实现游戏中的前后左右移动。
  • 跳跃检测:玩家只需快速向上跳跃,系统将自动检测并在游戏中触发跳跃动作。
  • 攻击控制:双手平行向前推,触发游戏中的攻击操作。
  • 头部控制视角:通过头部的上下左右移动,模拟鼠标的视角控制。
  • 自定义按键映射 通过用户界面(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()

五、总结与展望

本项目展示了通过计算机视觉与游戏控制相结合的可能性。通过MediapipePynput的结合,玩家可以通过身体动作直接控制游戏中的角色,为游戏带来更为直观和沉浸式的体验。

接下来可以做的优化包括:

  1. 动作识别优化: 通过使用滤波器(如卡尔曼滤波器)来减少姿态识别中的抖动。
  2. 支持更多游戏: 针对不同的游戏设计更加丰富的手势动作。
  3. 手势学习系统: 让用户自定义更多复杂的手势,并将其绑定到不同的按键操作上。

希望这篇博客能为大家提供一些启发,欢迎大家在评论区交流讨论!🎮👾


参考我的GitHub:SongxiangT/Game_Controller_Plug-in: This README includes a project description, features, installation steps, usage instructions, and more—all sprinkled with some emojis to keep it lively! (github.com)


 

作者信息:

  • 唐嵩翔(Master of Information Systems, 墨尔本大学) 
  • 沈昕(Master of Information Technology, 墨尔本大学)
  • 陈俊豪(Master of Information Systems, 墨尔本大学)

mediapipe是一个开源的跨平台框架,用于构建实时多媒体应用程序。它提供了一系列的机器学习和计算机视觉算法,可以用于各种应用,包括动作捕捉。 在Unity中使用mediapipe进行动作捕捉,可以实现将用户的动作实时应用到虚拟角色上。以下是一个简单的示例代码: ```csharp using Mediapipe.Unity; using UnityEngine; public class MotionCaptureExample : MonoBehaviour { public GameObject character; // 虚拟角色对象 private HandTrackingGraph handTrackingGraph; // 手部跟踪图 private void Start() { handTrackingGraph = gameObject.AddComponent<HandTrackingGraph>(); // 添加手部跟踪图组件 handTrackingGraph.Initialize(); // 初始化手部跟踪图 handTrackingGraph.OnHandsWithoutLandmarksOutput.AddListener(OnHandsWithoutLandmarksOutput); // 监听手部跟踪结果 } private void OnHandsWithoutLandmarksOutput(HandsWithoutLandmarks handsWithoutLandmarks) { if (handsWithoutLandmarks != null && handsWithoutLandmarks.Detections.Count > 0) { // 获取手部跟踪结果 var handDetection = handsWithoutLandmarks.Detections[0]; var handLandmarks = handDetection.HandLandmarks; // 根据手部跟踪结果更新虚拟角色的动作 character.transform.position = handLandmarks[0].ToVector3(); character.transform.rotation = handLandmarks[1].ToQuaternion(); } } private void OnDestroy() { handTrackingGraph.OnHandsWithoutLandmarksOutput.RemoveListener(OnHandsWithoutLandmarksOutput); // 移除监听 handTrackingGraph.Dispose(); // 释放资源 } } ``` 上述代码使用mediapipe的HandTrackingGraph组件进行手部跟踪,并将跟踪结果应用到虚拟角色的位置和旋转上。你可以根据自己的需求修改代码,实现更复杂的动作捕捉效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值