
演示视频
说明:由华南理工大学五人团队设计开发的3D水果切割游戏,目前未进行游戏打包发布,只能在Unity游戏引擎内编译运行体验,当前版本v1.0。
项目地址(阿里云盘):https://www.alipan.com/s/GAo2LaDTLXo 提取码:60fs(WinRAR解压后可运行,需要安装TuanjieHub和TuanjieEditor,建议Unity版本2022.3.61f1,编辑器版本1.6.7)
后续版本的代码更新地址(拉取到本地并覆盖项目原脚本文件):https://gitee.com/wawaforest4689/cutting_fruit
后续如有新增3D模型会通过阿里云盘分享。
本文基于原项目报告改版而成,原版本系5人共同写作完成。本文是关于该游戏项目开发的更详细的技术分享,同时包括一些项目的开发日志。
目录
[2.1 硬件需求](#2.1 硬件需求)
[2.2 软件需求](#2.2 软件需求)
[2.3 安装步骤 (Python端)](#2.3 安装步骤 (Python端))
[2.4 程序使用方式](#2.4 程序使用方式)
[2.5 交互操作](#2.5 交互操作)
[三、 实现方法与代码解析](#三、 实现方法与代码解析)
[3.1 视觉处理端 (Python --- hand_coord_detect)](#3.1 视觉处理端 (Python — hand_coord_detect))
[3.1.1 数据采集与初始化](#3.1.1 数据采集与初始化)
[3.1.2 手势分类和数据获取 (核心模块)](#3.1.2 手势分类和数据获取 (核心模块))
[2D 关键点推理(深度学习模型)](#2D 关键点推理(深度学习模型))
[掌根3D 世界坐标重建 (2D -> 3D )](#掌根3D 世界坐标重建 (2D -> 3D ))
[3.2 网络通信(Socket框架)](#3.2 网络通信(Socket框架))
[3.3 游戏交互端 (Unity --- 概念解析)](#3.3 游戏交互端 (Unity — 概念解析))
[3.3.1 场景与物理初始化](#3.3.1 场景与物理初始化)
[complete 层预制体](#complete 层预制体)
[slice 层预制体](#slice 层预制体)
[slicing 层预制体](#slicing 层预制体)
[3.3.2 数据接收与动作映射](#3.3.2 数据接收与动作映射)
[3.3.3 碰撞与反馈 (物理引擎)](#3.3.3 碰撞与反馈 (物理引擎))
[水果 --- 刀具碰撞切割逻辑设计](#水果 — 刀具碰撞切割逻辑设计)
[3.4 总结](#3.4 总结)
核心技术分享
(1)使用自编写脚本SFM算法+深度相机360°扫描构建水果点云模型,通过MeshLab处理导出为.obj模型;使用腾讯混元3D模型生成.obj水果完整体模型;使用MeshLab处理并二次型边缘下采样算法压缩获得.obj水果碎块体模型。


(2)基于Orbbec-Gemini RGBD深度相机获取三维信息;
(3)使用OpenCV-DNN的手掌识别模型识别掌根坐标、进行手部动作判断(0代表控制水果刀,1代表拉开抽屉,2代表合上抽屉);
(4)使用手指识别模型计算(平均)手掌角度、判别左右;
(5)使用记忆和容错模块存储上一帧的手部信息;
(6)使用Socket-TCP协议进行跨技术栈(Python客户端-Unity服务器)数据单向通信;
(7)Unity侧:设计6种水果(西瓜橙子葡萄苹果香蕉梨)+炸弹3D .obj模型,随机时刻随机位置生成抛体运动(生成参数如最小速度最大速度、每种水果最小体积最大体积可调、炸弹爆炸半径比可调),水果刀与手掌同步运动(灵敏度可调),抽屉在画布中随机生成与销毁(生成、销毁参数可调)并可由手部动作控制开合,用于接取水果。
v1.0存在问题:水果刀跟随运动不够丝滑;水果碎块体的切割(碰撞)效果无法按照预期调出;葡萄的动态销毁产生"拥堵";重力加速度不可自由设定,暂时使用系统的9.81;抽屉的销毁不符合预期且响应不够灵敏(和硬件质量有关);没有关卡设计和计时器、计分板。
一、项目背景
随着体感交互技术的发展,传统的键鼠或触屏操作已不能满足用户日益增长的沉浸式体验需求。本项目旨在利用先进的深度传感技术,结合成熟的游戏开发引擎,构建一个直观、自然的3D体感游戏原型。
本项目的核心目标是通过奥比中光(Orbbec)Gemini深度相机实时捕捉用户的手部动作和姿态,利用计算机视觉算法提取关键信息,并通过网络套接字(Socket)将数据实时传输至Unity 3D引擎。在Unity中,这些数据被映射为游戏内的交互指令,控制虚拟刀具进行切水果操作,或与场景元素(如抽屉)进行互动,从而实现"隔空切水果"的沉浸式体验。
二、环境安装和程序使用方式
2.1 硬件要求
- 深度相机:奥比中光 Gemini 系列深度相机。
- PC终端:运行Windows 10/11的电脑,需具备USB 3.0接口以保证相机数据传输速率。

2.2 软件要求
- Python 环境:Python 3.8 或以上版本。
- Unity 引擎:Unity 2020.3 LTS 或更高版本(用于运行游戏客户端),建议使用2022.3.61方版本,编辑器1.6.7。团结引擎最新版·官方下载 | Unity中国官网
2.3 安装步骤 (Python端)
- Python脚本和手掌手指识别模型均位于Assets/hand_coord_detect文件夹;
- 安装依赖库



2.4 程序使用方式
- 启动游戏端 (Unity):首先运行 Unity 项目。
- 启动视觉端 (Python):在 Python 环境中运行hand_coord.py。程序启动后,将初始化 Gemini 相机,开始捕捉深度流和彩色流。程序会自动识别视野内的手部,并在终端输出识别到的手势信息和坐标数据,同时开始尝试连接 Unity 端的 Socket 服务。
2.5 交互操作
- 站在相机前方适宜距离(0.3m --- 1.0m左右效果最佳)。
- 伸出手掌进行移动,控制游戏内的刀具位置。
- 通过推/拉/平面移动的动作来触发切水果动作或推拉抽屉动作。
三、 实现方法与代码解析
本项目分为两大核心部分:Python视觉处理端 和 Unity游戏交互端。两者通过 TCP协议( Python端使用socket库,Unity端使用System.Net和System.Net.Sockets 库)通信。
3.1 视觉处理端 (Python --- hand_coord_detect)
此部分的代码逻辑是整个项目的核心输入源,它是一个不断循环的流水线:获取图像 -> 检测手部 -> 分析手势 -> 计算参数 -> 发送数据。
3.1.1 数据采集与初始化
- SDK 初始化与设备打开:程序启动时,会加载 lib/ 目录下的 Orbbec SDK 动态库。代码会创建一个相机对象,尝试寻找并打开连接的 Gemini 设备。
- 视频流配置 (Stream Profile):代码会根据 config/ 中的设定,配置彩色流(Color Stream)和深度流(Depth Stream)的分辨率(例如 640x480)和帧率(例如 30 FPS)。
- D2C 对齐 (关键步骤):为了让 RGB 图像上的一个像素点准确对应到深度图上的同一个物理位置,代码必须开启 D2C (Depth to Color) 对齐功能。这是通过 SDK 提供的接口实现的,它会根据相机的内外参矩阵,将深度图重新投影到彩色相机的视角下
- 数据帧获取循环:在一个 while True 循环中,代码不断调用 SDK 的阻塞或非阻塞接口来获取最新的帧集合 (FrameSet)。然后从 FrameSet 中分离出对齐后的深度帧和彩色帧,转换为 OpenCV 可以处理的 NumPy 数组格式(BGR 图像和 16位深度图)。
3.1.2 手势分类和数据获取 (核心模块)
2D 关键点推理(深度学习模型)
- 获取的 BGR 彩色图像被送入OpenCV-DNN框架手掌检测器palm_detection_mediapipe_2023feb.onnx。
- 模型推理后,返回视野中检测到的每只手的 19维向量。

掌根3D 世界坐标重建 (2D -> 3D )
- 三维位矢 (x):手掌根部坐标(x,y,z)(xy由手掌识别模型计算得到,z由深度图索引得到,包含容错和记忆机制),xy用于控制游戏内水果刀在一个平面内运动,z作用于手部三种动作的判定。
- 手掌朝向(水果刀朝向):通过食指、中指、无名指、小拇指四指头的指根二维坐标计算算术平均值,再与掌根xy坐标一起计算arctan得到方向角(二维)。


3.2 网络通信(Socket框架)
- 模块对应:流程图中的紫色框体 "unity-python socket(TCP)通信"。
- 通过 TCP 协议将数据包发送到本地回环地址(127.0.0.1)和 Unity 监听的端口。
- 它将上一环节计算得到的坐标、手势类型、速度等数据打包成特定的字符串格式(例如 JSON 或特定分隔符的字符串:"x,y,gesture_type|v,rou,s" )。
- Python 端充当 Socket Client, Unity端充当Socket Server。
- 功能:将 Python 端计算出的结构化数据实时发送给 Unity 端。
3.3 游戏交互端 (Unity --- 概念解析)
3.3.1 场景与物理初始化
场景初始化
在Unity场景的初始化阶段,系统首先完成虚拟环境的构建与核心交互对象的实例化。为了模拟真实且自然的物体动态效果,场景中预置了抽屉模型及不同种类的水果模型。针对水果的运动轨迹,系统并未单纯依赖自由落体,而是通过编写自定义脚本赋予其特定的初始动量。具体而言,每个水果对象在实例化时都会被注入一个预设的三维初速度向量(Initial Velocity Vector),结合Unity物理引擎内置的重力系统,使其能够以符合物理规律的抛物线轨迹(Projectile Motion)进入玩家视野,从而增加游戏交互的动态感与可玩性。
水果物理初始化
在物理属性的精细化配置方面,为了平衡物理模拟的精确度与实时渲染的性能开销,系统针对高面数的水果模型进行了碰撞体优化。项目摒弃了计算昂贵的网格碰撞体(Mesh Collider),转而根据水果的几何特征,近似拟合为性能更优的球体碰撞体(Sphere Collider)或胶囊体碰撞体(Capsule Collider)。同时,为了增强物理反馈的真实感,项目开发了名为 PhysicalProperties 的物理属性计算脚本。该脚本在初始化阶段遍历模型网格的所有顶点数据,利用数学算法自动计算模型的近似体积,并结合预设的密度参数(如苹果密度设为 800 kg/m3),动态计算并赋值刚体(Rigidbody)的质量属性。这种基于体积与密度的质量计算方式,确保了不同体积的水果在与环境交互时具有符合直觉的惯性表现。

图2 苹果模型的两个胶囊碰撞体

图3 苹果模型的刚体属性和物理属性
刀具物理初始化
为实现水果切割的精准交互,对刀具对象(Knife)进行以下核心组件配置:
- 碰撞体配置:为刀具添加Box Collider和Mesh Collider,调整碰撞体范围至刀刃区域,确保仅刀刃接触水果时触发切割;
- 刚体配置:添加Rigidbody组件,勾选 "是运动学的"以避免刀具受物理重力影响偏移,碰撞检测模式设为 "连续",提升高速移动时的碰撞检测精度。
炸弹初始化
为实现爆炸后水果消失的核心交互,炸弹对象(bloom)需完成以下组件配置与预制体设计,确保功能完整性和视觉一致性:
- 碰撞体配置:为炸弹挂载 Sphere Collider组件,调整碰撞范围与炸弹模型尺寸匹配,确保碰撞体能够完整覆盖圆形炸弹区域。该碰撞体的触发逻辑为 "碰到任意物体即触发爆炸",碰撞检测目标包括刀、水果等游戏内交互对象。
- 刚体配置:添加 Rigidbody组件,使炸弹能够受重力、碰撞力等物理作用影响,实现自然下落、被撞击后移动等符合物理规律的运动状态。
- 控制脚本挂载:为炸弹预制体挂载tigger.cs脚本,该脚本是实现爆炸触发、范围计算、特效控制和物体销毁的核心逻辑载体,所有炸弹的爆炸行为均通过该脚本统一控制。
- 预制体组织:单独创建 "炸弹" 层级目录管理所有炸弹预制体,采用同一炸弹模型为基础,通过调整缩放属性区分不同规格炸弹(如 bloom0.6、bloom1.0、bloom2.5 等,数字对应缩放比例)。

图4 炸弹预制体组织

图5 炸弹模型
预制体层级结构设计与创建
为6种水果分别构建 "slicing 层 <--- complete 层(挂载预制体) + slice 层(挂载预制体)<----motion.cs(继承类的父类)" 三级独立预制体,为炸弹构建"tigger(1).cs"单层脚本(继承自motion.cs),挂载粒子系统组件。
complete 层预制体
挂载motion.cs和PhysicalProperties.cs脚本,选中水果完整体模型(未被刀具切割),添加Rigidbody、Collider等,水果的完全体会与其他物体碰撞,故需注意碰撞体的配置。可配置参数包括6类水果随机生成的最小体积、最大体积、6类水果的密度(均匀)、随机生成的最小初速度、最大初速度。

图6 苹果complete 层预制体
slice 层预制体
挂载motion.cs和PhysicalProperties.cs脚本,在 Hierarchy 面板选中水果切片模型,移除多余组件,仅保留Rigidbody、Mesh Filter、Mesh Renderer、Motion脚本和Physical Properties,为水果切片添加合适的物理性能,确保水果切片不再和其他物体发生碰撞,生成基础切片组件预制体。以苹果为例。

图7 苹果slice 层预制体
slicing 层预制体
添加切割逻辑脚本Slice.cs,是空对象。包括slice(碎块体)和complete(完整体)两个子组件,调整Transform参数,使缩放匹配父对象。在脚本参数中关联对应 slicing 层预制体,确保切割后可自动生成切片体,配置完成,形成完整的交互预制体。

图8 苹果slicing 层预制体
3.3.2 数据接收与动作映射
- 模块对应:Unity端的 Socket 接收模块及后续的粉色动作模块。
- 实现:Unity 中有一个常驻后台的 C# 脚本接收来自 Python 的 Socket 数据包并进行解析。
- 解析出的 (x, y) 世界坐标(非像素坐标,确保玩家游玩体验不因距离相机远近而有所不同)被映射到 Unity 世界坐标系,通过设置灵敏度参数(手动调整)调整映射关系,使用平滑操作更新虚拟刀具的位置。
- 解析出的 type(手势类型)用于触发动作(整数或枚举类型):0-控制刀:当手势为"切割"态时,水果刀运动同步对齐手掌根运动(加入平滑机制)。1-拉/2-推抽屉:当深度变化速度(相邻两帧的深度变化)超过阈值时,对应抽屉的拉开或者闭合操作。

3.3.3 碰撞与反馈 (物理引擎)
抽屉消失/物体回收
指物体(如水果碎块)掉落出屏幕边界后被销毁回收,以优化性能。在交互反馈环节,抽屉与水果的碰撞交互是物理系统的核心难点。由于抽屉模型具有中空的凹陷结构,直接使用默认的凸包(Convex)碰撞体会导致水果无法落入抽屉内部,而使用高精度的凹面网格碰撞体则会带来巨大的计算压力。为此,本项目采用了复合碰撞体(Compound Collider)方案,即利用五个独立的立方体碰撞体(Box Collider)分别构建抽屉的底板与四个侧壁。这种方案在保证物理轮廓准确性的同时,极大地降低了运算复杂度,有效杜绝了水果在高速运动中穿透抽屉模型的现象。为了解决刚体碰撞中常见的"回弹抖动"问题,系统将抽屉的刚体配置为运动学模式(Is Kinematic),并通过 Rigidbody.MovePosition 方法在物理帧中驱动其平滑移动,使其表现为具有无限质量的"无敌"容器,能够稳定地推开或承接水果。

图9 抽屉模型(绿色线框即为5个box collider)

图10 抽屉模型的刚体属性
此外,针对抽屉从"隐身"状态切换至"显示"状态时可能与场景中现存水果发生空间重叠(Spatial Superposition)的问题,本项目设计了一套防卡死弹出机制。当抽屉被激活显示时,系统会调用 Physics.OverlapBox 接口检测抽屉包围盒内部的物理实体。一旦检测到残留的水果对象,脚本将立即修正其位置坐标并施加一个向上的瞬时速度脉冲,将其强制弹出抽屉底部区域,从而解决了物理引擎因穿模导致的物体卡死问题。
水果 --- 刀具碰撞切割逻辑设计
切水果游戏的核心玩法就是刀碰到水果后对水果产生切割,为实现这一玩法,编写一个Slice脚本,Slice脚本作为水果切割的核心逻辑载体,通过 "预初始化 --- 碰撞切割 --- 资源清理" 三阶段实现完整切割流程,具体逻辑如下:
采用动态创建和销毁游戏对象的方式进行管理。进入画布时,从complete预制体初始化gobj1水果完整体。检测到与水果刀碰撞后,从SlicingPrefab预制体实例化切片体gobj2,继承水果完全体的位置 / 旋转信息,同时,销毁gobj1水果完整体;待水果碎片掉出屏幕后(如果在抽屉内则一直存在)销毁碎块体及当前对象。
炸弹碰撞逻辑设计
炸弹的碰撞触发、爆炸计算、物体销毁等逻辑均通过tigger(1).cs脚本实现,核心流程遵循 "碰撞检测→参数计算→特效生成→物理爆炸→对象销毁" 的链路,具体设计如下:
- 碰撞检测:通过OnCollisionEnter方法监听,当炸弹的 Sphere Collider 检测到与任意物体发生碰撞时,立即触发爆炸流程。若未分配爆炸特效预制体,则输出错误日志并终止流程,避免功能异常。
- 参数计算:通过CalculateBombSize方法计算炸弹实际尺寸,采用指数为 2.0 的公式放大尺寸对爆炸效果的影响,使不同规格炸弹的爆炸差异更显著。

图12 参数计算公式
-
特效生成:在碰撞接触点生成爆炸特效预制体实例,保持特效与碰撞位置的一致性。同时,针对不同大小的炸弹,调整特效大小。
-
物理爆炸:在TriggerExplosionPhysics方法中通过检测爆炸半径内的所有碰撞体,通过ShouldIgnoreTarget方法筛选需忽略的物体,包括炸弹自身、标签为 "Ground"和 "Player"的对象,避免误操作。对非忽略对象执行SetActive(false)操作,使其隐藏(而非直接销毁),后续可通过设置SetActive(true)重新启用,实现对象复用,优化性能。


图13 炸弹爆炸范围可视化

图14 ShouldIgnoreTarget方法

图15 TriggerExplosionPhysics方法
- 对象销毁:爆炸流程执行完毕后,通过Destroy(gameObject)销毁炸弹对象,避免残留。
3.4 总结
本项通过清晰的模块化设计,将复杂的体感游戏拆解为视觉计算(Python负责)和游戏逻辑(Unity负责)两个独立又协同的部分,充分利用了Python在AI领域的生态优势和Unity在实时渲染与物理模拟上的强大能力。流程图中的每一个节点在代码中都有对应的逻辑实现,共同支撑起整个交互系统的运行。
四、工程开发记录
(1)模型制备(调用工具)------使用腾讯混元3D制备炸弹/抽屉/水果刀的obj模型文件、使用meshlab制备六种水果碎块体obj模型文件;





(2)模型构建(编写代码:ptcloud_synthesis.py; pt_cloud.py)------构建初步的Structure From Motion算法,通过深度相机多张图片扫描水果构建3D点云模型并使用MeshLab转换为obj模型文件,目前进度(红色未完成): 2D相机(单目)姿态估计八点法(8x9矩阵构建)--本质矩阵提取--旋转矩阵/平移方向向量计算--基于RGBD全背景数据的2D to 3D 多视角点云集群计算--固定点数下采样(open3d库未实现FPS(最远点采样)算法,需要使用二分法体素下采样和随机下采样结合的方式控制点数恒定且相等)---平移方向向量的尺度均值法(非传统最小二乘法)估计---基于RGBD物体(水果)数据的2D to 3D 多视角点云集群计算(使用预制mask图片进行色度的"颜色滤波",并加入膨胀操作确保物体的像素连续性)--多视角点云拼接(仅相邻视角局部优化);

(3)手势计算代码完善
3.1 深度数据总是缺失?使用记忆法保留上次有效深度信息


3.2 手掌根部超出边界导致异常?设置有效手掌筛选


3.3 多手掌情形数据记忆出现混淆情况?根据handedness评分排序,保证左右手的id固定。

(4) Unity(服务器)和Python(客户端)的socket-TCP通信:解决(支持两只手同时控制)手部xy坐标、动作类型、手掌倾斜角度的数据跨端传输问题。
(5)基于鼠标控制水果刀改进的掌根坐标控制水果刀算法;
(6) 水果、炸弹的生成时间、生成频率控制与抛体运动的位置、速度控制;
(7)抽屉数量与随机点位生成控制;基于键盘控制抽屉改进的手势控制开闭算法;
(8)Unity类设计的层级结构完善与总场景调度器编写。

五、参考资料
1\] [Visual Studio 基础 之 VS 代码结构自动生成类图,类设计器的简单安装和使用_vs生成类图-CSDN博客](https://blog.csdn.net/u014361280/article/details/116428050 "Visual Studio 基础 之 VS 代码结构自动生成类图,类设计器的简单安装和使用_vs生成类图-CSDN博客") \[2\] [VisualSFM的配置与使用 \& MeshLab的网格生成与纹理添加-CSDN博客](https://blog.csdn.net/weixin_43042683/article/details/127930496 "VisualSFM的配置与使用 & MeshLab的网格生成与纹理添加-CSDN博客") \[3\] [【PCL】------ 点云配准ICP(Iterative Closest Point)算法_icp点云配准-CSDN博客](https://blog.csdn.net/sinat_52032317/article/details/130441840?spm=1001.2014.3001.5506 "【PCL】—— 点云配准ICP(Iterative Closest Point)算法_icp点云配准-CSDN博客") \[4\] [【Unity+Python】如何通过Socket进行通信_python与unity通信-CSDN博客](https://blog.csdn.net/weixin_42670590/article/details/137706090 "【Unity+Python】如何通过Socket进行通信_python与unity通信-CSDN博客") \[5\] [基于Python的Structure from Motion(SfM)三维重建实战教程-CSDN博客](https://blog.csdn.net/weixin_35826166/article/details/141575214 "基于Python的Structure from Motion(SfM)三维重建实战教程-CSDN博客")