I’m using the flutter_cube library to create a simple pool game, I’m trying to apply rotation to the balls as they move, in the current code if the angles in applyImpulse are 90 or 180 the ball rolls perfectly (the rotation), but when it’s rolling on the diagonal, something strange happens, the ball starts to rotate, but it doesn’t seem to be a correct rotation on the diagonal.
import 'package:flutter/material.dart';
import 'package:flutter_cube/flutter_cube.dart';
import 'package:flutter/scheduler.dart';
import 'dart:math';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Pool'),
),
body: Stack(
children: [Sinuca3DScene()],
),
backgroundColor: const Color.fromARGB(255, 13, 104, 250),
),
);
}
}
class Sinuca3DScene extends StatefulWidget {
@override
_Sinuca3DSceneState createState() => _Sinuca3DSceneState();
}
class _Sinuca3DSceneState extends State<Sinuca3DScene>
with TickerProviderStateMixin {
Object? ballModel;
Vector3 ballPosition = Vector3(0.0, 0.0, 0.0);
Vector3 ballVelocity = Vector3(0, 0, 0);
double force = 30.0;
double rotationFactor = 200;
late Ticker _ticker;
late Scene _scene;
bool loaded = false;
double friction = 0.97;
@override
void initState() {
super.initState();
_ticker = createTicker((_) => _update());
_ticker.start();
ballModel = Object(fileName: 'assets/ball.obj');
ballModel?.scale.setValues(1, 1, 1);
}
void _update() {
setState(() {
_updateBallPosition(const Duration(milliseconds: 10));
});
}
void _applyImpulse(Vector3 impulse) {
ballVelocity += impulse;
}
void rotateBall(Duration deltaTime) {
if (ballModel != null && ballVelocity.length > 0.01) {
double rotationAngle =
ballVelocity.length * deltaTime.inMilliseconds / 1000.0;
rotationAngle *= rotationFactor * deltaTime.inMilliseconds / 1000.0;
double rotationX = ballVelocity.y * rotationAngle;
double rotationY = ballVelocity.x * rotationAngle;
ballModel!.rotation += Vector3(rotationY, -rotationX, 0);
ballModel!.rotation = Vector3(normalizeAngle(ballModel!.rotation.x),
normalizeAngle(ballModel!.rotation.y), 0);
ballModel!.updateTransform();
}
}
double normalizeAngle(double angle) {
angle = angle % 360;
if (angle < 0) angle += 360;
return angle;
}
void _updateBallPosition(Duration deltaTime) {
ballPosition += ballVelocity * deltaTime.inMilliseconds.toDouble() / 1000.0;
ballModel?.position.setFrom(ballPosition);
double wallMargin = 0.5;
if (ballPosition.x >= (7 - wallMargin) ||
ballPosition.x <= (-13 + wallMargin)) {
ballVelocity.x = -ballVelocity.x;
}
if (ballPosition.y >= (10 - wallMargin) ||
ballPosition.y <= (-10 + wallMargin)) {
ballVelocity.y = -ballVelocity.y;
}
if (ballVelocity.length <= 1) {
ballVelocity.setZero();
}
rotateBall(deltaTime);
ballVelocity *= friction;
}
@override
void dispose() {
_ticker.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Cube(
onSceneCreated: (Scene scene) {
_scene = scene;
loaded = true;
scene.world.add(ballModel!..position.setFrom(ballPosition));
scene.camera.fov = 50;
scene.world.scene!.camera.position.setValues(1, 1, 30);
},
interactive: false,
),
Positioned(
bottom: 20,
left: 20,
child: FloatingActionButton(
onPressed: () {
double impulseMagnitude = force;
//double intValue = Random().nextDouble() * 180;
double degreesAngle = 90;
Vector3 impulse = Vector3(
impulseMagnitude * cos(radians(degreesAngle)),
impulseMagnitude * sin(radians(degreesAngle)),
0);
_applyImpulse(impulse);
},
child: Icon(Icons.arrow_forward),
),
),
GestureDetector(
onPanUpdate: (details) {
print(details);
},
)
],
);
}
}
I’ve already tried calculating using other parameters, but the code above was the best result I got