Hi! I'm trying to make a tank turret with an angular velocity motor joint (so with a physic body on both the tank and the turret), on the whole it works well, I calculate the speed of the turret according to its rotational distance with the direction of the camera, and put it in the target velocity of the velocity joint but I have a slight problem: When the tank rotates, the turret starts to spin. I tried to modify the springFrequency and springDamping values of the Hinge joint associated with the angular velocity motor, but nothing works. I would like my joint to be very stiff, and to be only slightly impacted (if at all) by the rotation of the tank body. If anyone knows a way to do this (without completely blocking the rotation of the turret when the tank is turning), or knows a better way to rotate the turret, let me know.

I'm curious to know how you handle it in ECS, because that's what I was doing in mono too. I have two problems: Since I don't put joints, the turret is a child of the hull's physic body, which means that its collider is merged into the hull's compound collider, and which means that rotating the turret's localTransform is not enough to rotate its collider. But, this can be solved by also rotating the collider in the compound apparently (I'll look into that). What concerns me most is what SteveeHavok said in this thread, where he was against this solution: PhysicsCollider not updating with parent changes. - Unity Forum. I'm particularly worried about the collision misses, since turret collisions are very important in a tank game (and some tanks have a pretty big turret).

I may have looked at the physic package documentation too quickly, but so far, from my understanding, if I put a collider as a child of a physic body (in a subscene), it is automatically merged into a compound collider with the other colliders associated with that physic body. If you have a solution to avoid this I'm interested.

@Tigrian you can use a fixed joint and then update BallAndSocket (my bad, it's FixedAngle) constraint orientation every frame. But I'm not sure if ECS actually allows you to modify joints (I use Physics directly with no ECS). Updating compound collider is not a big deal either, technically you can just rebuild colliders every frame (I believe you plan only several tanks in the world) or modify collider API to be able to update BVH/masses. But I'd add to the SteveeHavok's the fact, that such transform changes happen outside of the solver cycles, implying unwanted penetrations that can make physics go nuts.

@n3b I would have loved such a solution to work, but I just checked, unfortunately no way to change the orientation of a ballAndSocket constraint. I like the idea though, I'm looking at the list of available constraints, see if there is one that can do the job. Otherwise, I managed in a hacky way to almost have my turret not spin: I added to the joint motor angular speed value 2 times the y-angle speed of the tank's hull (to compensate exactly for the velocity given to the turret by the tank's rotation). This works perfectly except when the tank stops rotating, the turret is then very slightly wobbly for a second, which gives an unnatural feeling, especially when the rotation of the hull stops abruptly. I'll try to find where it comes from. This effect is undesirable enough to me to abandon the idea of the joint, if the reason of this wobbliness is not to be found. And as @TheOtherMonarch recommend not to use a joint, I'm still looking for a solution to make the turret collider move without issues : In my case, hitting or not hitting the turret of a tank with a shell is essential in gameplay, it is excluded that collisions can be missed, or recorded wrongly.

You can do this in a number of different ways. I am still experimenting myself with which way is best. I will probably end up with something similar to what @scottjdaley does during baking. But I cannot give you an answer what is best just yet. https://forum.unity.com/threads/phy...being-merged-with-parents-on-convert.1310672/ Compound colliders cause other issues as well such as not knowing what part of the tank gets hit by a projectile. So it absolutely needs to be worked around.

@Tigrian I just checked and found this method (see attachment) so you definitely can update joint constraints @TheOtherMonarch and you definitely can acquire the child of compound collider from hit info Code (CSharp): var key = hit.ColliderKey; collider.Value.GetChild(ref key, out var childCollider); or child index Code (CSharp): key.PopSubKey(collider.Value.NumColliderKeyBits, out var childIndex);

@Tigrian oh, it's even simpler - you only need to update BodyAFromJoint or BodyBFromJoint depending on which one is the turret, both have setters (i.e. no need to update constraints)

That is very limited. The real issue I am having with compound colliders was that I did not want all hitboxes to be part of the same ridgebody and collide with the terrain etc.

That is what collision filters are for. You can have different filters for every single child of compound collider - whether those are wheels or hitboxes.

For the SetConstraints, I'm limited to the property that are in the constraint struct, i.e. in the case of a ballAndSocket, these (target is only there for the motorized joints). Code (CSharp): public static Constraint BallAndSocket(float3 impulseEventThreshold, float springFrequency = DefaultSpringFrequency, float springDamping = DefaultSpringDamping) { return new Constraint { ConstrainedAxes = new bool3(true), Type = ConstraintType.Linear, Min = 0.0f, Max = 0.0f, SpringFrequency = springFrequency, SpringDamping = springDamping, MaxImpulse = impulseEventThreshold, Target = float3.zero }; } So, no way to change it's orientation. And, in the case of BodyAFromJoint, this might do, but I am limited to a Body Frame. Code (CSharp): new BodyFrame { Axis = new float3(0f,1f,0f), PerpendicularAxis = new float3(1f, 0f, 0f), Position = float3.zero } I don't know what the perpendicular axis do, maybe rotating this one on the Y axis will do the trick, but I'm sceptical. Alternatively, the BodyFrame can be converted to a RigidTransform, but as the name implies, I think this is for read-only purposes (it's not an actual transform but rather a copy of the transform, this code is from the BodyFrame struct) : Code (CSharp): public RigidTransform AsRigidTransform() => new RigidTransform(ValidateAxes(), Position);

Sorry, I gave you a false hint here. The first thing - I mixed up BallAndSocket and FixedAngle (the first one limits linear motions). The second - these two constraints have no info about orientation, but they do keep orientation and position of two bodies using BodyAFromJoint and BodyBFromJoint as a reference. BodyFrame has an implicit conversion from RigidTransform, so this should be a straightforward solution Code (CSharp): joint.BodyBFromJoint = new RigidTransform(newOrientationInJointSpace, positionInJointSpace); Alternatively you could use PositionMotor joint (not the velocity one). It contains 3 constraints: Code (CSharp): m_Constraints = new ConstraintBlock3 { Length = 3, A = Constraint.MotorPlanar(target, math.abs(maxImpulseOfMotor)), B = Constraint.FixedAngle(), C = Constraint.Cylindrical(0, float2.zero) } where the first one is responsible for the second body orientation, which you can update like this (it will keep the rest 2 constraints intact): Code (CSharp): joint.SetConstraints(new FixedList512Bytes<Constraint>{Length = 1, [0] = Constraint.MotorPlanar(targetAngle, maxImpulse)});

Each child keeps its original filter (you can check that by collider.Value.GetCollisionFilter(childColliderKey)), also BoundingVolumeHierarchy keeps each leaf union for Aabb overlap queries edit: my bad, CompoundCollider BVH doesn't keep filters yet children do

Nice, i'll try the implicit conversion as RigidTransform, this should do the trick. I'll tell you what my results are. About the position motor, did you meant the rotational motor? Because the position motor modifies position, so inputing an angle into its target would not make sense. If it is the case, well, unfortunately, this is what I tried first, and I encounter a problem listed in the known issues of the physics ecs package (Unity Physics overview | Unity Physics | 1.0.0-pre.65 (unity3d.com)): The turret was constantly rotating and did not react to changes in the targetAngle. But even if it did work, I don't know how to limit the turret rotation speed precisely if I just input a target angle to the joint (historical tanks usually have a very well defined maximum turret rotation speed).

ah, right, it's the rotation one Code (CSharp): joint.SetConstraints(new FixedList512Bytes<Constraint>{Length = 1, [0] = Constraint.MotorTwist(target, math.abs(maxImpulseOfMotor))}); You mentioned that you used "angular velocity motor joint", that one is different Code (CSharp): public enum ConstraintType : byte { /// <summary> An enum constant representing the linear type. </summary> Linear, /// <summary> An enum constant representing the angular type. </summary> Angular, /// <summary> An enum constant representing the rotation motor type. </summary> RotationMotor, /// <summary> An enum constant representing the angular velocity motor type. </summary> AngularVelocityMotor, /// <summary> An enum constant representing the position motor type. </summary> PositionMotor, /// <summary> An enum constant representing the linear velocity motor type. </summary> LinearVelocityMotor } I'm not sure if you actually hit that known issue, or it's a misconfiguration case. Regarding max velocity, as I can see in RotationMotor constraint there is a maxImpulse field which is self explanatory MotionVelocity contains this method Code (CSharp): public void ApplyAngularImpulse(float3 impulse) { AngularVelocity += impulse * InverseInertia; } means Code (CSharp): var maxImpulse = (requiredMaxAngularVelocity / InverseInertia)[indexOfRotationAxis]; joint.SetConstraints(new FixedList512Bytes<Constraint>{Length = 1, [0] = Constraint.MotorTwist(targetAngle, math.abs(maxImpulse))}); Assuming that you have to rotate body while there is a user input, I'd go with a fixed joint instead. But there is a catch - you have to cover cases when there is an obstacle that blocks turret rotation yourself, otherwise turret will start rotating tank. In such cases something like motor with limited impulse is preferable.

Which means that instead of just ignoring colliders tons of extra work / random access needs to happen hardly very optimized. Just for raycasts. Not to mention that impacts with colliders will happen and affect the ridgebody solver. What a terrible design by Unity. And don't get me started on use cases involving a hitbox inside another hitbox. For example, an engine hitbox inside an armor hitbox.

Setup with angular velocity motor joint, with compensation by using a the y Angular velocity (from the PhysicsVelocity component) of the hull added to the to the target turret speed. It works, but the turret has a little weird wobbly bug when the tank stop turning (in video, you can see it at 0:9, 0:17, 0:21 and 0:31): Setup with Rotational Motor joint, with maxVelocity set to math.radians(24), and thus maxImpulse to math.abs((maxVelocity/InverseInertia)[indexOfRotationAxis]), target is set directly as the camera pivot angle. The bug of the known issues seams to appear only when the turret goes to the rear of the vehicle (which correspond to the change in target angle from 2*PI to 0 or 0 to 2*PI ), it just becomes mad and starts spinning. As you can see in the video, the turret move instanly with camera change, which made me think that the maxvelocity must be multiplied by a deltaTime. by reducing the maxvelocity, the turret start to rotate around the target angle (a bit too far to the left, then to the right, and so on).

That's true, quaternions represent rotations up to PI radians, so rotation motor will always produce invalid correction when initial error (orientation before step) and future error (after step) are on the opposite sides Code (CSharp): // Calculate the error, adjust by tau and damping, and apply an impulse to correct it float futureError = CalculateError(futureMotionBFromA); float solveError = JacobianUtilities.CalculateCorrection(futureError, InitialError, Tau, Damping); Code (CSharp): float CalculateError(quaternion motionBFromA) { float angle = 2.0f * math.acos(motionBFromA.value.w); quaternion targetAsQuaternion = quaternion.Euler(Target); float targetAngle = 2.0f * math.acos(targetAsQuaternion.value.w); return angle - targetAngle; } Regarding the wobbliness with velocity motor, are you using default values for spring frequency and damping or custom ones? If the former, how do you calculate velocity for the motor?

I'm using the default ones, I'll show you some code on how I calculate the velocity target (but it might be a mess to read): The camera is first rotated by input, and a component store the camera pivot (which has the same position of the turret) x and y rotation : Code (CSharp): var x = -data.Value.y * speed.Value * SystemAPI.Time.DeltaTime; var y = data.Value.x * speed.Value * SystemAPI.Time.DeltaTime; angle.Angle.x = ((angle.Angle.x + x)%360f); angle.Angle.y = ((angle.Angle.y + y)%360f); Then, I compute the turret y-localRotation, by comparing the angle between the forward direction of the turret and the forward direction of the hull, Code (CSharp): //FloatUtilities.Angle is float3 equivalent to Vector3.Angle var angle = FloatUtilities.Angle(localTransform.ValueRO.Forward(), _componentLookup[hullEntityReference.ValueRO.Value].Forward()); var sign = -math.sign(math.dot(_componentLookup[hullEntityReference.ValueRO.Value].Up(), math.cross(localTransform.ValueRO.Forward(),_componentLookup[hullEntityReference.ValueRO.Value].Forward()))); angleY.ValueRW.Angle = angle * sign; Afterwards, I calculate the turret speed based on a turn rate that is computed with previous turnrate Code (CSharp): //_cameraAngle is a componentLookup of CameraAngle, the angle stored in first step. //AngleY is the angle of turret calculated above. var turretRingTargetAngle =_cameraAngle[cameraPivot.ValueRO.CameraPivot].Angle.y - angleY.ValueRO.Angle; var sign = math.sign(turretRingTargetAngle); turretRingTargetAngle = math.abs(turretRingTargetAngle); var currentSlowdownAng = math.abs(data.ValueRO.RotationSpeed * speed.ValueRO.PreviousTurnRate.y) * data.ValueRO.Deceleration; float targetTurnRate = math.clamp( turretRingTargetAngle / (data.ValueRO.RotationSpeed * Time.fixedDeltaTime + currentSlowdownAng), 0f, 1f) * sign; var turnRate = turretRingTargetAngle > currentSlowdownAng ? FloatUtilities.MoveTowards(speed.ValueRO.PreviousTurnRate.y, targetTurnRate, SystemAPI.Time.fixedDeltaTime /data.ValueRO.Acceleration) : FloatUtilities.MoveTowards(speed.ValueRO.PreviousTurnRate.y, targetTurnRate, SystemAPI.Time.fixedDeltaTime / data.ValueRO.Deceleration); speed.ValueRW.PreviousTurnRate.y = turnRate; //Write in TurretRotationSpeedComponent. speed.ValueRW.Speed = data.ValueRO.RotationSpeed * turnRate; And then I apply in a different system (Because I want this system to only rely on TurretRotationSpeed component, to have AI Tank). Notice that in order for the turret not to spin, I add 2 times the angular velocitity of the hull physicsbody. Code (CSharp): var constraints = joint.ValueRW.GetConstraints(); var c0 = constraints[0]; c0.Target = new float3(_turretRotationSpeedLookup[bodyPair.ValueRO.EntityA].Speed + 2f * _physicsVelocityLookup[bodyPair.ValueRO.EntityB].Angular.y,0f,0f); constraints[0] = c0; joint.ValueRW.SetConstraints(constraints);

It may be worth noting that the input is done in classic update (InitializationSystemGroup), while the update of the joints is done in fixedStep, in this group : Code (CSharp): [UpdateInGroup(typeof(PhysicsSimulationGroup))] [UpdateAfter(typeof(PhysicsCreateBodyPairsGroup))] [UpdateBefore(typeof(PhysicsCreateContactsGroup))] As I'm trying to debug this, I got some inconsistent result due to the fact these don't update at the same time, even though I set the framerate to 60 fps, and the physic step to 0.167

I'd go with something like this: Code (CSharp): const float maxTurretVelocity = 0.5f; // rad/sec // camera orientation in turret space var c = math.mul(math.conjugate(turretWorldTransform.rot), cameraWorldTransform.rot); // Extract the angle of rotation around the y-axis var forward = math.forward(c); var deltaAngle = math.atan2(forward.x, forward.z); // max required velocity deltaAngle *= invertedDeltaTime; var requiredTurretVelocity = math.clamp(deltaAngle, -maxTurretVelocity, maxTurretVelocity); // represents relative angular velocity between two bodies var constraintTarget = new float3(0, requiredTurretVelocity, 0); It doesn't take into account the fact that every joint applies impulse to both bodies, so there will be some undershoot after step but it should be negligible. edit: I've updated angle extraction

Much much simpler indeed , thanks to that, I've deleted one system, two componentLookup, some components and the weird block of calculation with the previous turnRate. I really love how elegant this solution is, as you get the same effect with turret deceleration as it get closer to target But unfortunately, there is still the wobbling when the tank stop turning abruptly, I've tried a solution that reduce the effect but costs me one more componentLookup. I gather the input data for moving the tank from a struct called TankMoveData (this can be user input or AI Inputs), which is on the Hull entity. By doing this componentLookup to get the input as soon as the rotation input get to 0, I can multiply by a fine tune compensation value the target velocity, and this for a couple physics steps (Ideally, I make it stop when the angular velocity y of the hull has reached 0), limiting the over/undershoot. I'm not a fan of this way of doing things, as it does not completely remove the wobbling (sometimes it is still there). Please tell me if you find the wobbling in the video disturbing from a player point of view. Anyway, here is the code, can certainly be improved, so tell me what you think about it, and if you believe this kind of solution is worth it, or what alternative could be used to remove this wobbling : Code (CSharp): if (!_turretRotationSpeedLookup.HasComponent(bodyPair.ValueRO.EntityA)) continue; var speed = _turretRotationSpeedLookup[bodyPair.ValueRO.EntityA].Speed; var hullAngularVelocityY = _physicsVelocityLookup[bodyPair.ValueRO.EntityB].Angular.y; var tankMoveData = _tankMoveDataLookup[bodyPair.ValueRO.EntityB]; var target = speed+ 2f * hullAngularVelocityY; if (compensation.ValueRO.IsTurning && math.abs(tankMoveData.Value.x) < 0.05f) { if (math.abs(hullAngularVelocityY) < 0.01f || compensation.ValueRO.StepCount > compensation.ValueRO.MaxStepCount) { compensation.ValueRW.IsTurning = false; compensation.ValueRW.StepCount = 0; } else { compensation.ValueRW.StepCount++; target *= compensation.ValueRO.CompensationValue; } } else { compensation.ValueRW.IsTurning = math.abs(tankMoveData.Value.x)>0.05f; } var constraints = joint.ValueRW.GetConstraints(); var c0 = constraints[0]; c0.Target = new float3(target,0f,0f); constraints[0] = c0; joint.ValueRW.SetConstraints(constraints);

So without this smoothing do you see any wobbling when hull is not moving? Are there overshoots by the turret itself? Might make sense to integrate turret orientation to compensate velocity change Code (CSharp): // we do this before sim, so hullAngularVelocity is the one that is expected to be after sim step, i.e. with user input applied var angularVelocity = hullAngularVeloctiy + turretAngularVelocity; var turretOrientation = turretWorldTransform.rot; Integrator.IntegrateOrientation(ref turretOrientation, angularVelocity, timestep); // camera orientation in turret space var c = math.mul(math.conjugate(turretOrientation), cameraWorldTransform.rot); ... Also I'm not yet sure about this but you might want to track the sign change of requiredTurretVelocity. i.e. if in prev frame it was negative and in current it's positive or vice versa. This happens on overshoots during hull velocity change so it could be necessary to increase clamp by hull velocity delta. edit: probably I'm wrong about hullAngularVeloctiy. the turretAngularVelocity is what affects future turret position with no constraints applied, and we want to compensate it with hull velocity change - so probably hullAngularVelocity should be the difference between prev and future hull velocity.

Without this smoothing, I get about the same result as in the video called "Turret Angular velocity motor + compensation in targetVelocity". That means no overshoot that can be catch by human eye when the hull is not moving or moving in a straigh line (with camera that moved to different direction and turret moving of course). When the hull is turning, I believe the turret is a bit lagging behind the hull movement : it must be about 1 degree or less behind ("behind" in hull angular velocity direction) camera's camera pivot Y rotation <=> constantly undershooting by 1 degrees. It is not very noticeable, so I'll do debugs to know if it is really the case. And of course, when the hull stop turning, the turret overshoot by a couple degrees in front ("front" meaning the turret overshoot in the same direction than previous hull angular velocity direction), which is really noticeable. Actually, it is not exact same result than in previous video : In previous video the turret was wobbly, meaning once the overshoot at occured, the turret started to slightly "vibrate", going a bit right then a bit left... and so on. Now the turret overshoot first by a couple degree, and then just correct itself, going back to the right rotation, without "overshooting while correcting the overshoot". I hope I'm clear in my explanations, if not I'll do an other video . I'll try the different Integrator method setups you suggested, see if one works. I'll track the sign aswell.

Sounds like expected flaws of velocity motor when you actually need the rotation one if it worked correctly. You are calculating error prior to simulation and are trying to compensate velocity based on that, while there are many things may happen during solver iterations which not being taken into account (including collisions). If it's critical - there is a way to fix rotation motor before Unity released fix, but you'd need to copy and edit physics package.

Not critical at all, I have other area I can expand on, and come back to the turret when rotational motor is fix. But I'm curious anyway, if it is not a too much laborious fix for you to write, I'll be glad to know it. I'm okay with dealing with a copy of physic package for test purposes, but if it takes more than a couple lines to change in physics package, don't bother, I can wait the fix.

Well I think error calculation has to be relative to target quaternion, so RotationMotorJacobian.CalculateError instead of Code (CSharp): float CalculateError(quaternion motionBFromA) { float angle = 2.0f * math.acos(motionBFromA.value.w); quaternion targetAsQuaternion = quaternion.Euler(Target); float targetAngle = 2.0f * math.acos(targetAsQuaternion.value.w); return angle - targetAngle; } should be Code (CSharp): float CalculateError(quaternion motionBFromA) { var targetAsQuaternion = quaternion.Euler(Target); var delta = math.mul(math.conjugate(targetAsQuaternion), motionBFromA); var forward = math.forward(delta); return math.atan2(forward.x, forward.z); } (targetAsQuaternion can be built during Build stage) The math guys probably can find a less expensive solution given that the rotation axis is constant, but I don't want to spend time on that (in your case it won't bottleneck you). What makes me think about your case more and more is the fact that the joint defines max impulse like a limit that motor can apply, while we ignore friction of the ring system completely. i.e. when hull stops rotating the friction should stop turret rotation too, but the joint negative impulse is limited by joint constraint, so it can't catch up with the hull. And the problem is that maxImpulse is the same solver input as velocity is - you can't adjust it between solver iterations. So if for example hull hits obstacle during step - it will stop rotating, but the turret will try to catch up using same maxImpulse - that will introduce another overshoot.

Thank you very much for your help and your detailled explanations. Both you and TheOtherMonarch helped me very much on this. I see now where the problem lies, so I won't take more of your time. I will try different setups, including the no joint one and will post the final results when I get something good

You are welcome! I think I got it after all With functional rotation motor - maxImpulse defines friction of the system, and the velocity limit can be implemented similar to example of velocity motor, but instead of clamped veloctiy you calculate clamped per step distance that will gradually increase/decrease constraint target.

@n3b : Thanks for the great contribution here and sorry for the delay in joining in. I have just confirmed that there is indeed an issue with the relative velocity calculation in the angular velocity motor. We will be looking at this issue internally. @Tigrian: I apologize for the issues this has caused you in authoring your tank. However, for this particular case, the position-based RotationMotor might actually be better suited since it can not suffer from any velocity drift by definition, ensuring that your turret always remains perfectly fixed at the target angle relative to the chassis. The RotationMotor acts like a spring-damper and has a target angle while the AngularVelocityMotor is a pure damper and has a target velocity. Therefore, the RotationMotor will always attempt to reach a target angle, while the angular velocity motor will only try to honor the specified target velocity. This allows you to drive the rotation motor to the exact location you desire and prevent it from changing this location unless the game requires it. So you could indeed manually update the target angle in the RotationMotor gradually over time using a simple first order euler integrator as was mentioned above: newAngle = oldAngle + timeStep * desiredRotationSpeed @n3b: As for the fix you suggest above, may I ask what version of the code this is from? We have made some fixes in some recent release with the rotation motor and the code you are showing might be the old one. In the latest version, the RotationMotorJacobian.CalculateError function is as follows: Code (CSharp): private float CalculateError(quaternion motionBFromA) { // Calculate the relative joint frame rotation quaternion jointBFromA = math.mul(math.mul(math.inverse(MotionBFromJoint), motionBFromA), MotionAFromJoint); // extract current axis and angle between the two joint frames ((Quaternion)jointBFromA).ToAngleAxis(out var angle, out var axis); // filter out any "out of rotation axis" components between the joint frames and make sure we are accounting // for a potential axis flip in the to-angle-axis calculation. angle *= axis[AxisIndex]; return math.radians(angle) - Target; }

@Tigrian : Above you pointed out a known issue with the rotational motor that is mentioned in the documentation: I believe that this has been fixed in the last few releases and is a leftover (see latest code above). We will confirm and update the known issues sections accordingly.

Hey @daniel-holz thanks for the info! I do indeed use outdated version of physics since it is heavily modified and also my own constraints instead of motors. But I believe Tigrian mentioned they're on the latest and one of the videos demonstrates issues with PI+ angles

@Tigrian: Could you please share the code you are using to update the target rotation angle and the package version? I'd like to reproduce the behaviour you are experiencing on my end. The wrap around in the Unity Physics joint might simply not be accounted for in the internal joint error calculation, which causes a singularity around 2 pi (360 degrees). I want to make sure though that this is not induced by the target angle calculation in some way, e.g. setting a >360 degree target angle after the first wrap around which would potentially induce a requested full 360 spin in the joint. That could explain the fact that the joint starts spinning faster and faster. To reproduce this exactly, your angle control code would be very helpful. Btw, in order to reduce the wobblyness when the turret starts or stops rotating you could limit the acceleration as well as the velocity in your controller by calculating the new rotation velocity using a single Euler integration (new_v = old_v + timestep * max_acceleration), then clamping the velocity to some maximum (new_v = max(new_v, max_v) and with the new velocity updating your turret angle with a second Euler integration (new_angle = old_angle + timestep * new_v). When stopping the turret simply do the same but with a negative acceleration and negative minimum velocity.

Hi! using this with the rotational motor joint allowed to remove the wobbling when the tank stop turning, so this is gone. I still have a problem when the turret cross 2PI or -2PI, where it starts to spin.This might be due to my implementation, so here is the code (thanks to n3b for the required velocity code), I had the turret "vibrating" at rest without the 0.05f factor in the clamp, that is why it is there. : Code (CSharp): [RequireMatchingQueriesForUpdate] [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))] [UpdateAfter(typeof(PhysicsSystemGroup))] [BurstCompile] public partial struct TurretRotationSystem : ISystem { private ComponentLookup<LocalToWorld> _localToWorlds; [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate<TurretJointData>(); _localToWorlds = SystemAPI.GetComponentLookup<LocalToWorld>(true); } [BurstCompile] public void OnDestroy(ref SystemState state) { } [BurstCompile] public void OnUpdate(ref SystemState state) { _localToWorlds.Update(ref state); foreach (var (joint,turretAngle,data,cameraPivot, bodyPair) in SystemAPI.Query<RefRW<PhysicsJoint>, RefRW<TurretJointAngle>,RefRO<TurretJointData>,RefRO<CameraDrivenTurret>, RefRO<PhysicsConstrainedBodyPair>>()) { // camera orientation in turret space var c = math.mul(math.conjugate(_localToWorlds[bodyPair.ValueRO.EntityA].Rotation), _localToWorlds[cameraPivot.ValueRO.CameraPivot].Rotation); // Extract the angle of rotation around the y-axis var forward = math.forward(c); var deltaAngle = math.atan2(forward.x, forward.z); // max required velocity deltaAngle *= 1/SystemAPI.Time.fixedDeltaTime; var requiredTurretVelocity = math.clamp(0.05f * deltaAngle, -data.ValueRO.RotationSpeed, data.ValueRO.RotationSpeed); // represents relative angular velocity between two bodies var targetAngle = (turretAngle.ValueRO.Angle + requiredTurretVelocity * SystemAPI.Time.fixedDeltaTime); var constraints = joint.ValueRW.GetConstraints(); var c0 = constraints[0]; c0.Target = new float3(targetAngle,0f,0f); constraints[0] = c0; joint.ValueRW.SetConstraints(constraints); turretAngle.ValueRW.Angle = targetAngle; } } }

That's great to hear! Looking at your code you probably need to make the angle wrap around at 2pi and -2pi. So just do an fmod of your new target angle with 2pi before setting it in the joint and you should be good.

Hum, there is still a problem, it spins a little bit before returning to normal. I'll try to post you a video. I'm doing this Code (CSharp): var targetAngle = (turretAngle.ValueRO.Angle + requiredTurretVelocity * SystemAPI.Time.fixedDeltaTime) %(2*math.PI);

Hmmm... Yeah looks like this alone doesn't work. There seems to be still some sort of singularity around 2pi in the joint. We'll have to investigate this. Maybe just to confirm that the values you are providing make sense could you log them and post them here around the point that singularity occurs?

Hi @Tigrian . Thanks for that video. It's clear that there is some bug around +-2pi. We logged the issue internally and will look into it. Likely the error that is calculated when moving past the +-2pi mark (previous location not yet past 2pi and new target location past +-2pi) will jump unintentionally. So we might just need to modify the CalculateError function to account for this crossing point. Probably just using a "DeltaAngle" function. Edit: I just tried this real quick and it shows promise. Could you try to replace the last line in the RotationMotorJacobian.CalculateError function by this line please? Code (CSharp): return math.radians(Mathf.DeltaAngle(math.degrees(Target), angle)); That would replace the line Code (CSharp): return math.radians(angle) - Target; That might do it until we have finalized the fix.

Hi @daniel-holz. Here is a video of the result, there is a problem around 0 (in fact around +- 2Pik without the fmod). It does not happend all the time, but it is there. I'm sorry the angle is not displayed anymore, I remove the setup to display it, If needed I can do it again, I just wanted to warn you quickly about it. I've tried setting the target to 0 if it comes too close to 0, and the wobbling happend aswell, even though the target is exaclty 0.

Ok. I think we are getting somewhere. We have this issue logged internally and we will continue working out the fix. Your control code is very helpful for this purpose. Thank you. In the meantime, I was also able to reproduce the angular velocity motor issue internally, and we are also closing in on a fix for this one. I'll keep you posted with the developments.

@Tigrian Quick update here. We found the issue with the angular velocity motor not correctly dealing with the relative velocity between two moving rigid bodies, and the fix is completed. It will be added to a future release. This should fix your original tank case.

Hi @daniel-holz, I can confirm that the fix works, my turret takes into account the angular velocity of the hull with the angular velocity joint. No need for strange compensation with the angular velocity of the hull, and no more wobbling when the tank stops turning. I just enter an angular velocity value and my turret turns at that value, whatever the hull does. It's perfect, thank you very much for the fix.