You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用flutter_cube实现球体按特定角度持续旋转?

解决flutter_cube中使用updateTransform实现球体持续旋转的问题

问题分析

你当前的代码里直接在每帧给ball.rotation.x和y累加固定角度值,再调用updateTransform却无效,核心原因有两点:

  1. 未基于**时间增量(deltaTime)**计算角度增量,导致旋转速度不受控,还可能因帧速率波动出现异常
  2. flutter_cube的updateTransform是通过rotation的绝对角度值生成变换矩阵,但你需要确保更新后的rotation能正确驱动矩阵更新,同时场景能感知到变换变化

修正方案

1. 基于时间增量计算旋转角度

把固定角度累加改成基于每帧的时间差计算增量,保证旋转速度稳定。同时明确rotation存储的是角度值(因为updateTransform内部会用radians()转成弧度)。

2. 确保场景刷新

调用updateTransform后,需要通知场景更新变换,或者通过setState触发组件重绘,让Cube组件感知到对象变换的变化。

修正后的完整代码

import 'package:flutter/material.dart';
import 'package:flutter_cube/flutter_cube.dart';
import 'package:flutter/scheduler.dart';
import 'dart:math';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Pool'),
        ),
        body: const Stack(
          children: [_Scene()],
        ),
        backgroundColor: Color.fromARGB(255, 13, 104, 250),
      ),
    );
  }
}

class _Scene extends StatefulWidget {
  const _Scene();

  @override
  _SceneState createState() => _SceneState();
}

class _SceneState extends State<_Scene> with TickerProviderStateMixin {
  late Object ball;
  late Scene _scene;
  late Ticker _ticker;
  // 旋转速度(度/秒)
  final double rotationSpeed = 90.0;
  // 记录上次帧时间,用于计算时间增量
  DateTime? _lastFrameTime;

  @override
  void initState() {
    super.initState();
    _ticker = createTicker((elapsed) => _update(elapsed));
    _ticker.start();

    ball = Object(fileName: 'assets/ball.obj');
    ball.scale.setValues(1, 1, 1);
    // 初始化rotation值
    ball.rotation.setValues(0, 0, 0);
  }

  void _update(Duration elapsed) {
    final currentTime = DateTime.now();
    if (_lastFrameTime == null) {
      _lastFrameTime = currentTime;
      return;
    }
    // 计算时间增量(秒)
    double delta = currentTime.difference(_lastFrameTime!).inMilliseconds / 1000.0;
    _lastFrameTime = currentTime;

    _updateBallPosition(delta);
    // 触发重绘,让Cube组件感知对象变换
    setState(() {});
  }

  void _updateBallPosition(double deltaTime) {
    // 基于时间增量计算角度增量
    double angleDelta = rotationSpeed * deltaTime;

    // 更新对角线旋转角度(同时绕X和Y轴旋转)
    ball.rotation.x += angleDelta;
    ball.rotation.y += angleDelta;

    // 更新变换矩阵
    ball.updateTransform();
    // 直接通知场景更新,确保变换立即生效
    _scene.update();
  }

  @override
  void dispose() {
    _ticker.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Cube(
          onSceneCreated: (Scene scene) {
            _scene = scene;
            scene.world.add(ball);

            scene.camera.fov = 50;
            scene.world.scene!.camera.position.setValues(0, 0, 10);
          },
          interactive: false,
        ),
      ],
    );
  }
}

关键修改点

  • 新增_lastFrameTime记录上一帧时间,计算时间增量(deltaTime),保证旋转速度在不同帧率下一致
  • 将旋转角度累加改为基于rotationSpeed * deltaTime的增量计算,避免角度疯涨和速度不稳定
  • 调整_update方法,使用实际时间差替代固定的16ms假设
  • 在_updateBallPosition中调用ball.updateTransform()后,同时调用_scene.update()确保场景立即刷新变换
  • 初始化ball.rotation的初始值,避免未定义的初始状态

自定义对角线旋转轴的补充

如果需要绕任意自定义对角线轴旋转,可以通过Quaternion直接构建旋转,再同步到对象的变换中:

// 示例:绕自定义对角线轴(1,1,0)旋转
void _updateBallPosition(double deltaTime) {
  double angleDelta = rotationSpeed * deltaTime;
  // 创建绕(1,1,0)轴旋转的四元数
  Quaternion rotationQuat = Quaternion.axisAngle(Vector3(1,1,0), radians(angleDelta));
  // 将当前旋转与新旋转叠加
  ball.transform.rotateQuaternion(rotationQuat);
  // 同步rotation值到transform,保持两者状态一致
  ball.rotation.setFromQuaternion(ball.transform.rotation);
  ball.updateTransform();
  _scene.update();
}

内容的提问来源于stack exchange,提问作者Jhevi P.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:27:25