Flutter ARCore中如何获取相机位置并检测与障碍物的碰撞?
解决方案
获取相机位置
要获取实时相机位置,需启用ARCore的帧更新监听,具体调整如下:
- 在
ArCoreView配置中开启enableUpdateListener: true - 通过
ArCoreController的onFrame回调获取每一帧的相机姿态,从中提取位置信息
相机与障碍物碰撞检测
ARCore没有直接提供相机碰撞检测功能,我们可以通过空间距离计算实现:
- 维护一个活跃障碍物节点的列表,方便后续遍历检测
- 将相机近似为一个小立方体(半径取0.1米),障碍物的碰撞边界取其尺寸的半长轴
- 实时计算相机中心与障碍物中心的欧氏距离,若距离小于两者半径之和则判定为碰撞
修改后的完整代码
import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:arcore_flutter_plugin/arcore_flutter_plugin.dart'; class ArScreen extends StatefulWidget { @override _ARScreenState createState() => _ARScreenState(); } class _ARScreenState extends State<ArScreen> { late ArCoreCube cubeRenderable; late ArCoreController arCoreController; int count = 0; Vector3? cameraPosition; // 存储实时相机位置 final List<ArCoreNode> activeObstacles = []; // 活跃障碍物列表 Timer? obstacleTimer; // 单独管理障碍物生成定时器 @override void initState() { super.initState(); initializeModels(); } void initializeModels() { cubeRenderable = ArCoreCube( materials: [ ArCoreMaterial( color: Color.fromARGB(255, 255, 0, 0), ), ], size: Vector3(50, 50, 50), ); } bool isARViewReady = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('AR App'), ), body: Stack( children: [ ArCoreView( onArCoreViewCreated: _onArCoreViewCreated, enablePlaneRenderer: true, enableTapRecognizer: true, enableUpdateListener: true, // 开启帧更新监听 ), if (!isARViewReady) Center( child: CircularProgressIndicator(), ), // 显示实时相机位置 if (cameraPosition != null) Positioned( top: 20, left: 20, child: Text( "相机位置: ${cameraPosition!.x.toStringAsFixed(2)}, ${cameraPosition!.y.toStringAsFixed(2)}, ${cameraPosition!.z.toStringAsFixed(2)}", style: TextStyle(color: Colors.white, fontSize: 12), ), ), ], ), ); } void _onArCoreViewCreated(ArCoreController controller) { print("onArCoreViewCreated (plane)"); arCoreController = controller; _setupTapGesture(); _setupPlaneDetection(); _setupFrameListener(); // 注册帧监听获取相机位置 setState(() { isARViewReady = true; }); } void _setupFrameListener() { arCoreController.onFrame = (ArCoreFrame frame) { // 从帧中获取相机姿态,提取位置 final cameraPose = frame.cameraPose; if (cameraPose != null) { setState(() { cameraPosition = cameraPose.translation; }); // 每帧执行碰撞检测 _checkCollisions(); } }; } void _checkCollisions() { if (cameraPosition == null || activeObstacles.isEmpty) return; const double cameraRadius = 0.1; // 相机近似半径(米) for (var obstacle in activeObstacles) { final obstaclePosition = obstacle.pose?.translation; final obstacleSize = (obstacle.shape as ArCoreCube).size; if (obstaclePosition == null) continue; // 计算障碍物的半长轴(取最大维度的一半) final obstacleHalfSize = max(max(obstacleSize.x, obstacleSize.y), obstacleSize.z) / 2; // 计算欧氏距离 double distance = sqrt( pow(cameraPosition!.x - obstaclePosition.x, 2) + pow(cameraPosition!.y - obstaclePosition.y, 2) + pow(cameraPosition!.z - obstaclePosition.z, 2), ); // 判断碰撞 if (distance < cameraRadius + obstacleHalfSize) { print("碰撞发生!障碍物: ${obstacle.name}"); // 这里可以添加碰撞后的逻辑(比如游戏结束、扣分等) _handleCollision(); break; } } } void _handleCollision() { // 示例:停止障碍物生成,弹窗提示 obstacleTimer?.cancel(); showDialog( context: context, builder: (ctx) => AlertDialog( title: Text("碰撞!"), content: Text("你撞到障碍物了!"), actions: [ TextButton( onPressed: () { Navigator.pop(ctx); // 可选:添加重置游戏逻辑 }, child: Text("确定"), ), ], ), ); } void _setupTapGesture() { arCoreController.onPlaneTap = _handleOnPlaneTap; print("onPlaneTap set"); } void _setupPlaneDetection() { arCoreController.onPlaneDetected = _handleOnPlaneDetected; print("onPlaneDetected set"); } void _handleOnPlaneDetected(ArCorePlane plane) { print("Plane detected: ${plane.type}"); } void _handleOnPlaneTap(List<ArCoreHitTestResult> hits) { print("tapped on plane"); final hit = hits.first; final cube = ArCoreCube( size: Vector3(0.1, 0.1, 0.1), materials: [ArCoreMaterial(color: Colors.red)], ); // 避免重复创建定时器 obstacleTimer?.cancel(); obstacleTimer = Timer.periodic(Duration(milliseconds: 100), (timer) { final node = ArCoreNode( shape: cube, position: hit.pose.translation + Vector3( 0, 0, -timer.tick * 0.01, ), rotation: hit.pose.rotation, name: "cube$count", ); arCoreController.addArCoreNode(node); activeObstacles.add(node); // 添加到活跃列表 print("${node.name} added"); count++; // 延迟删除障碍物并从列表移除 Future.delayed(Duration(milliseconds: 1000), () { arCoreController.removeNode(nodeName: node.name); activeObstacles.remove(node); print("${node.name} removed"); }); }); } @override void dispose() { obstacleTimer?.cancel(); arCoreController.dispose(); super.dispose(); } }
关键修改说明
- 相机位置获取:开启
enableUpdateListener,通过onFrame回调实时获取相机姿态的位置参数 - 碰撞检测逻辑:
- 维护
activeObstacles列表跟踪所有未被删除的障碍物 - 每帧计算相机与障碍物的空间距离,对比碰撞阈值判断是否碰撞
- 维护
- 定时器优化:用单独的
obstacleTimer管理障碍物生成,避免重复触发 - 碰撞处理:添加了碰撞后的弹窗提示,可扩展为游戏逻辑(如结束、重置)
内容的提问来源于stack exchange,提问作者SaraSpears
相关产品推荐
相关产品推荐

