public void HitBall(int playerId, float angle, float forceFrac) { //if (playerId != CurrentPlayer) throw new ArgumentException($"Wrong player ID: {playerId}. Expected: {CurrentPlayer}"); if (forceFrac < 0 || forceFrac > 1) { throw new ArgumentException($"Invalid force fraction value: {forceFrac}"); } var ballBody = PlayerBalls[playerId].Body; var forceVec = Vector3.forward * (MaxStrokeForce * forceFrac); ballBody.AddForce(Quaternion.Euler(0, angle, 0) * forceVec, ForceMode.Impulse); var trajectory = new Trajectory(FrameDeltaTime); var physicsScene = SimulationScene.GetPhysicsScene(); for (int nSteps = 0; !BallsStopped(); ++nSteps) { physicsScene.Simulate(FrameDeltaTime); trajectory.AddFrame(Frame.Extract(PlayerBalls)); if (nSteps > 3000) { Debug.Log($"Ball0Position: {PlayerBalls[0].transform.position}, " + $"Ball0Velocity: {PlayerBalls[0].Body.velocity}, " + $"Ball0Sleeping: {PlayerBalls[0].Body.IsSleeping()}"); Debug.Log($"Ball1Position: {PlayerBalls[1].transform.position}, " + $"Ball1Velocity: {PlayerBalls[1].Body.velocity}, " + $"Ball1Sleeping: {PlayerBalls[1].Body.IsSleeping()}"); throw new ApplicationException("Simulation is too long."); } } Events.Enqueue(new Event.PlayTrajectory(trajectory, playerId)); CurrentPlayer = (CurrentPlayer + 1) % NumberOfPlayers; }