// mark args as ReadOnly as much as possible public void Execute(Entity ent, int index, [ReadOnly] ref Translation position, [ReadOnly] ref Rotation rotation) { DynamicBuffer <AutoShootBuffer> shootBuffer = autoShootBuffers[ent]; DynamicBuffer <TimePassed> buffer = timePassedBuffers[ent]; for (int i = 0; i < shootBuffer.Length; ++i) { AutoShoot shoot = shootBuffer[i].val; // getting the right TimePassed TimePassed timePassed = buffer[shoot.timeIdx]; TimePassed volleyCount = buffer[shoot.volleyCountIdx]; timePassed.time += dt; if (timePassed.time > shoot.startDelay) { float actualTime = timePassed.time - shoot.startDelay; // EXTRA: add recovery for bullets not fired exactly when they should // fire if above period and not on cooldown while (actualTime >= shoot.period && volleyCount.time < shoot.numVolleys) { // shoot the pattern Fire(ent, index, ref position, ref rotation, ref shoot, actualTime); volleyCount.time += 1; timePassed.time -= shoot.period; actualTime = timePassed.time - shoot.startDelay; } // on cooldown, wait for time to pass to reset if (actualTime >= shoot.cooldownDuration && volleyCount.time == shoot.numVolleys) { volleyCount.time = 0; timePassed.time -= shoot.cooldownDuration; } } // writing updates out buffer[shoot.timeIdx] = timePassed; buffer[shoot.volleyCountIdx] = volleyCount; } }
// Lets you convert the editor data representation to the entity optimal runtime representation public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { Assert.IsTrue(period > 0); // add a TimePassed component int timeIdx = TimePassedUtility.AddDefault(entity, dstManager); int volleyCountIdx = TimePassedUtility.AddDefault(entity, dstManager); Entity bulletEnt = conversionSystem.GetPrimaryEntity(bullet); AutoShoot shootData = new AutoShoot { // The referenced prefab will already be converted due to DeclareReferencedPrefabs. // so just look up the entity version in the conversion system bullet = bulletEnt, startDelay = startDelay, cooldownDuration = cooldownDuration, period = period, pattern = pattern, count = count, sourceOffset = new float3(sourceOffset.x, sourceOffset.y, 0), numVolleys = numVolleys, angle = math.radians(angle), aimStyle = aimStyle, centerAngle = math.radians(centerAngle), timeIdx = timeIdx, volleyCountIdx = volleyCountIdx, moveStatsIdx = AutoShootUtility.GetOrAddMovementIdx(movementStats), damageStatsIdx = AutoShootUtility.GetOrAddDamageIdx(damageStats) }; DynamicBuffer <AutoShootBuffer> buffer = AutoShootUtility.GetOrAddBuffer(entity, dstManager); buffer.Add(new AutoShootBuffer { val = shootData }); }
private void Fire(Entity ent, int index, [ReadOnly] ref Translation position, [ReadOnly] ref Rotation rotation, [ReadOnly] ref AutoShoot shoot, float time) { float interval; quaternion fireDirection = quaternion.identity; float3 shooterForward = math.forward(rotation.Value); float3 spawnPos = position.Value + shoot.sourceOffset; float3 playerPos = (playerPositions.Length > 0) ? playerPositions[ent.Index % playerPositions.Length].Value : new float3(0, 0, 0); switch (shoot.aimStyle) { case AimStyle.Forward: fireDirection = math.normalize(rotation.Value); break; case AimStyle.Player: if (playerPositions.Length > 0) { fireDirection = quaternion.LookRotation( shooterForward, math.normalize(playerPos - spawnPos)); } else // no player targets, just use rotation { fireDirection = math.normalize(rotation.Value); } break; } switch (shoot.pattern) { case ShotPattern.FAN: if (shoot.count == 1) { InitBulletPosRot(spawnPos, shooterForward, fireDirection, shoot.centerAngle, time - shoot.period, shoot.moveStatsIdx, playerPos, out float3 pos, out quaternion rot); CreateBullet(index, shoot.bullet, pos, rot, shoot.moveStatsIdx, shoot.damageStatsIdx); } else { // space between shots is angle / (count - 1) to have // shots on edges interval = shoot.angle / (shoot.count - 1); float halfAngle = shoot.angle / 2; for (float rad = -halfAngle; rad <= halfAngle; rad += interval) { InitBulletPosRot(spawnPos, shooterForward, fireDirection, rad + shoot.centerAngle, time - shoot.period, shoot.moveStatsIdx, playerPos, out float3 pos, out quaternion rot); CreateBullet(index, shoot.bullet, pos, rot, shoot.moveStatsIdx, shoot.damageStatsIdx); } } break; case ShotPattern.AROUND: interval = (float)(2 * math.PI / shoot.count); for (float rad = 0.0f; rad < 2 * math.PI; rad += interval) { InitBulletPosRot(spawnPos, shooterForward, fireDirection, rad + shoot.centerAngle, time - shoot.period, shoot.moveStatsIdx, playerPos, out float3 pos, out quaternion rot); CreateBullet(index, shoot.bullet, pos, rot, shoot.moveStatsIdx, shoot.damageStatsIdx); } break; } }