- @Kuba have provided an excellent solution. Here we follow he's idea and use another approach to get the particle tracing.
SeedRandom[1];
reflect[vector_,
normal_] = -(vector - 2 (vector - Projection[vector, normal])) //
Simplify;
R = RegionUnion @@
Table[Ball[{Cos[i], Sin[i], 0}, .4], {i, 0, 2 Pi, Pi/6.}];
R2 = RegionBoundary[DiscretizeRegion[R, MaxCellMeasure -> 0.0001]];
dist = RegionDistance[R2];
proj = RegionNearest[R2];
pt0 = RandomPoint[R, 1][[1]];
v0 = {1., 1., 1.};
d0 = 0.01*Norm[v0];
sol = NDSolveValue[{x''[t] == 0, y''[t] == 0,
z''[t] == 0, {x[0], y[0], z[0]} == pt0, {x'[0], y'[0], z'[0]} ==
v0, WhenEvent[
dist@{x[t], y[t], z[t]} <=
d0, {Derivative[1][x][t], Derivative[1][y][t],
Derivative[1][z][t]} ->
reflect[{Derivative[1][x][t], Derivative[1][y][t],
Derivative[1][z][t]}, {x[t], y[t], z[t]} -
proj@{x[t], y[t], z[t]}]]}, {x[t], y[t], z[t]}, {t, 0, 20},
MaxStepSize -> 0.01];
ani = Animate[
Show[Graphics3D[{{FaceForm[Opacity[.2], Darker@Cyan], EdgeForm[],
R2}}, Boxed -> False],
ParametricPlot3D[sol, {t, 0, c}, Mesh -> {{c}},
MeshStyle -> {AbsolutePointSize[8], Red},
Method -> {"BoundaryOffset" -> False},
PlotStyle -> {Opacity[.9], White}, PlotPoints -> 400,
PerformanceGoal -> "Quality", PlotRange -> All] /. Line -> Arrow,
ViewPoint -> {1, 1, .8}, Background -> Gray], {c, $MachineEpsilon,
20}, AnimationRate -> 1, ControlPlacement -> Bottom]

- Test another type of bounce and another obstacle surfaces.
reflect[vector_,
normal_] = -(vector - 2 (vector - Projection[vector, normal])) //
Simplify;
surf = ExampleData[{"Geometry3D", "UtahTeapot"}, "Region"];
cube = TransformedRegion[Cuboid @@ Transpose@RegionBounds[surf],
ScalingTransform[{1.2, 1.2, 1.2}, RegionCentroid[surf]]] //
DiscretizeRegion // RegionBoundary;
reg = RegionUnion[surf, cube];
dist = RegionDistance[reg];
proj = RegionNearest[reg];
pt0 = RegionCentroid[reg];
v0 = {1., 1., 0.2};
d0 = 0.01*Norm[v0];
sol = NDSolveValue[{x''[t] == 0, y''[t] == 0,
z''[t] == -9.8, {x[0], y[0], z[0]} == pt0, {x'[0], y'[0], z'[0]} ==
v0, WhenEvent[
dist@{x[t], y[t], z[t]} <=
d0, {Derivative[1][x][t], Derivative[1][y][t],
Derivative[1][z][t]} ->
reflect[{Derivative[1][x][t], Derivative[1][y][t],
Derivative[1][z][t]}, {x[t], y[t], z[t]} -
proj@{x[t], y[t], z[t]}]]}, {x[t], y[t], z[t]}, {t, 0, 20},
MaxStepSize -> 0.001];
ani = Animate[
Show[Graphics3D[{{FaceForm[Opacity[.2], Cyan], EdgeForm[],
surf}, {FaceForm[Opacity[.3], LightGray], EdgeForm[], cube}},
Boxed -> False],
ParametricPlot3D[sol, {t, 0, c}, Mesh -> {{c}},
MeshStyle -> {AbsolutePointSize[8], Red},
Method -> {"BoundaryOffset" -> False},
PlotStyle -> {Opacity[.9], White}, PlotPoints -> 400,
PerformanceGoal -> "Quality", PlotRange -> All] /. Line -> Arrow,
Background -> LightGray], {c, $MachineEpsilon, 20},
AnimationRate -> 1, ControlPlacement -> Bottom]
