I’m trying to debug movement issues with my Netcode for Entities project.
Hopefully someone here can help me ![]()
Here is a video showing the issue:
My player rapidly flickers between the Client (predicted) and Server positions.
The Server position is always behind the Client (predicted) position, seems normal.
But (I think) my cube is also flickering between rendering at the server position and the client position.
The project is expanded from the Networked Cube example.
Original post
This is the inspector on the player prefab:
I am trying to move the player with PhysicsVelocity instead of directly modifying the LocalTransform.
This is the system that applies movement to the cube.
[BurstCompile]
[UpdateInGroup(typeof(PredictedFixedStepSimulationSystemGroup))]
public partial struct CubeMovementSystem : ISystem {
ComponentLookup<Ability> ability_lookup;
[BurstCompile]
public void OnCreate(ref SystemState state) {
ability_lookup = SystemAPI.GetComponentLookup<Ability>(true);
}
[BurstCompile]
public void OnUpdate(ref SystemState state) {
foreach (
var (
transform,
phys_velocity,
input,
movement,
abilities
) in SystemAPI.Query<
RefRW<LocalTransform>,
RefRW<PhysicsVelocity>,
RefRW<NetPlayerInput>,
RefRW<Movement>,
DynamicBuffer<Abilities>
>().WithAll<GhostOwnerIsLocal>()
) {
ability_lookup.Update(ref state); // Update the state of ComponentLookups
float speed = movement.ValueRO.speed;
float speed_max = movement.ValueRO.speed_max;
// Code related to dash ability removed for the sake of readability
// All logic is conditional on input and bug happens without using dash
// Enforce physics constraints
///////////////////////////////////////////////////////////////////
phys_velocity.ValueRW.Linear.y = 0;
transform.ValueRW.Rotation = quaternion.Euler(
new float3(0, transform.ValueRO.Rotation.value.y, 0)
);
if (math.length(phys_velocity.ValueRO.Linear) <= math.EPSILON) {
phys_velocity.ValueRW.Linear = float3.zero;
}
// Player input movement
///////////////////////////////////////////////////////////////////
bool has_player_movement = input.ValueRO.move.Equals(float2.zero);
if (!!!has_player_movement) {
movement.ValueRW.Velocity.Linear.x += input.ValueRO.move.x * (speed * SystemAPI.Time.DeltaTime);
movement.ValueRW.Velocity.Linear.z += input.ValueRO.move.y * (speed * SystemAPI.Time.DeltaTime);
}
// Enforce movement limits
///////////////////////////////////////////////////////////////////
if (math.length(movement.ValueRW.Velocity.Linear) > speed_max) {
movement.ValueRW.Velocity.Linear = math.normalizesafe(movement.ValueRO.Velocity.Linear) * speed_max;
}
// Apply movement to physics
///////////////////////////////////////////////////////////////////
if (math.length(movement.ValueRW.Velocity.Linear) > math.EPSILON) {
phys_velocity.ValueRW.Linear.x += movement.ValueRO.Velocity.Linear.x;
phys_velocity.ValueRW.Linear.z += movement.ValueRO.Velocity.Linear.z;
}
}
}
}
This is the component that holds the player input:
[GhostComponent(PrefabType=GhostPrefabType.AllPredicted)]
public struct NetPlayerInput : IInputComponentData {
public float2 move;
public InputEvent dash;
}
Component holding player-driven movement:
[GhostComponent(PrefabType=GhostPrefabType.AllPredicted)]
public struct Movement : IComponentData {
public PhysicsVelocity Velocity;
public float speed;
public float speed_base; // Allow modifying speed
public float speed_max;
public float speed_max_base; // Allow modifying max speed
}
Camera follow system:
public partial class CameraSystem : SystemBase {
protected override void OnUpdate() {
float3 world_position = float3.zero;
foreach (var (
_,
player,
local_transform,
local_to_world,
entity
) in SystemAPI.Query<
GhostOwnerIsLocal,
RefRO<Player>,
RefRO<LocalTransform>,
RefRO<LocalToWorld>
>().WithEntityAccess()) {
// Debug.Log($"Local Position: {local_transform.ValueRO.Position}");
world_position = local_transform.ValueRO.Position;
}
foreach (var (
camera,
camera_settings,
local_to_world
) in SystemAPI.Query<
MainCamera,
RefRW<MainCameraSettings>,
RefRW<LocalToWorld>
>()) {
camera.look_at = world_position;
camera.Transform.LookAt(world_position);
camera.Transform.position = SystemAPI.GetComponent<LocalToWorld>(camera_settings.ValueRO.Position).Position;
// Debug.Log($"World Position: {world_position}");
}
}
}
Camera components:
public class MainCamera : IComponentData {
public Transform Transform;
public float3 look_at;
}
public struct MainCameraSettings : IComponentData {
public Entity Pivot;
public Entity Position;
}
The MainCameraSettings hold references to Entities that are initialized by a proxy component just like in the ECSSamples PhysicsSamples. They are Child GameObjects of the Player prefab with only a transform – then baked.
Package versions:
Entities: 1.0.16
Netcode for Entities: 1.0.17
Unity Physics: 1.0.16
Mathematics: 1.2.6
EDIT: In this case, the results were caused by the camera follow and was completely unrelated to the movement. See posts below.
