I’m trying to spawn a lot of bullets in a circle around the player. However, when using Play Mode Tools and setting it to (Broadband [WIFI] / Regional [50th Percentile]). I see a lot more bullets being spawned, then I would expect. It does clean them up given enough time.
I’ve tried different solutions, looking at sample code and so on. Hope anyone can help figure out my mistake.
Removing the [GhostField]
from WeaponFireTick
makes it look more correct, but the samples I’ve found says that it should be there, which also seems right?
The projectile ghost authoring component:
The main components:
public struct PlayerInput : IInputComponentData
{
public InputEvent Fire;
}
public struct Weapon : IComponentData
{
public uint TicksBetweenShots;
public float3 Offset;
public Entity Prefab;
}
public struct WeaponFireTick : IComponentData
{
[GhostField] public NetworkTick Value;
}
[GhostComponent(PrefabType = GhostPrefabType.AllPredicted, SendTypeOptimization = GhostSendType.OnlyPredictedClients)]
public struct Velocity : IComponentData
{
[GhostField(Quantization=100, Smoothing=SmoothingAction.InterpolateAndExtrapolate)] public float3 Value;
}
The input system:
[UpdateInGroup(typeof(GhostInputSystemGroup))]
public partial class PlayerInputSystem : SystemBase
{
private InputActions _input;
protected override void OnCreate()
{
_input = new InputActions();
_input.Enable();
}
protected override void OnUpdate()
{
var fire = _input.Player.Attack.IsPressed();
foreach (var playerInput in SystemAPI.Query<RefRW<PlayerInput>>().WithAll<GhostOwnerIsLocal>())
{
playerInput.ValueRW = default;
if (fire)
playerInput.ValueRW.Fire.Set();
}
}
}
Weapon Fire system:
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[BurstCompile]
public partial struct WeaponFireSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var networkTime = SystemAPI.GetSingleton<NetworkTime>();
if (!networkTime.IsFirstTimeFullyPredictingTick)
return;
var ecb = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged);
foreach (var (weapon, weaponFireTick, transform, ghostOwner) in SystemAPI.Query<RefRO<Weapon>, RefRW<WeaponFireTick>, RefRO<LocalTransform>, RefRO<GhostOwner>>().WithAll<Simulate>())
{
if (weapon.ValueRO.IsFiring && (!weaponFireTick.ValueRO.Value.IsValid || networkTime.ServerTick.IsNewerThan(weaponFireTick.ValueRO.Value)))
{
weaponFireTick.ValueRW.Value = networkTime.ServerTick;
weaponFireTick.ValueRW.Value.Add(weapon.ValueRO.TicksBetweenShots);
var velocity = state.EntityManager.GetComponentData<Velocity>(weapon.ValueRO.Prefab).Value;
for (var i = 0; i < 32; i++)
{
var wr = math.mul(quaternion.AxisAngle(math.up(), i * (math.PI2 / 32)), transform.ValueRO.Rotation);
var wp = transform.ValueRO.Position + math.mul(wr, weapon.ValueRO.Offset);
var e = ecb.Instantiate(weapon.ValueRO.Prefab);
ecb.SetComponent(e, LocalTransform.FromPositionRotationScale(wp, wr, 0.4f));
ecb.SetComponent(e, new Velocity { Value = math.mul(wr, velocity) });
ecb.SetComponent(e, new GhostOwner { NetworkId = ghostOwner.ValueRO.NetworkId });
}
}
}
}
}
Projectile Move System:
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[BurstCompile]
public partial struct ProjectileMoveSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var dt = SystemAPI.Time.DeltaTime;
foreach (var (transform, velocity) in SystemAPI.Query<RefRW<LocalTransform>, RefRO<Velocity>>().WithAll<Simulate>())
{
transform.ValueRW.Position += velocity.ValueRO.Value * dt;
}
}
}