using System; using System.Collections.Generic; using System.Linq; using System.Text; using BITFALL.Bullet; using UnityEngine; using BITKit; using BITKit.Entities; using Cysharp.Threading.Tasks; using UnityEditor; using UnityEngine.Animations; using UnityEngine.UIElements; namespace BITFALL { [Serializable] public record InstanceBullet : SpawnBullet { public Vector3 Velocity; public Vector3 currentPos; public float currentSpeed = 64; public float ElapsedTime; } [Serializable] public class BulletServiceSingleton : IBulletService { public void Spawn(SpawnBullet bullet)=>BulletService.Spawn?.Invoke(bullet); } public class BulletService : MonoBehaviour, IBulletService { public static Action Spawn; [Header(Constant.Header.Settings)] [SerializeField] private LayerMask layerMask; [SerializeField] private Material material; [Header(Constant.Header.Providers)] [SerializeField, SerializeReference, SubclassSelector] private IEntitiesService entitiesService; [SerializeField, SerializeReference, SubclassSelector] private INetProvider netProvider; [SerializeField, SerializeReference, SubclassSelector] private IDamageService damageService; [Header(Constant.Header.InternalVariables)] private readonly List instances = new(); private readonly RaycastHit[] _raycastHits = new RaycastHit[16]; private const float dragCoefficient = 0.045f; // 子弹的阻力系数 private const float bulletMass = 0.015f; // 子弹的质量,单位:千克 private Mesh _mesh; private void Start() { _mesh = GetComponent().mesh; Spawn += SpawnBullet; } private void OnDestroy() { Spawn -= SpawnBullet; } private void Update() { var camera = Camera.main; if (!camera) return; var rot = camera.transform.rotation * Quaternion.Euler(0, 180, 0); foreach (var bullet in instances.ToArray()) { if (bullet.ElapsedTime >= 0.04f) { // Graphics.DrawMesh(_mesh, bullet.currentPos, bullet.rot, material, // LayerMask.NameToLayer("TransparentFX")); var martix = Matrix4x4.TRS( bullet.currentPos, rot, Vector3.one + Vector3.one * Mathf.Clamp(bullet.ElapsedTime, 0, 16)); Graphics.DrawMesh(_mesh, martix, material, LayerMask.NameToLayer("TransparentFX")); } } } private void FixedUpdate() { foreach (var bullet in instances.ToArray()) { var size = Physics.RaycastNonAlloc(bullet.currentPos, bullet.Velocity, _raycastHits, Vector3.Distance(default,bullet.Velocity) * Time.fixedDeltaTime, layerMask); var validHit = false; foreach (var raycastHit in _raycastHits.Take(size).OrderBy(x => Vector3.Distance(bullet.pos, x.point))) { if (!IsValidHit(raycastHit, bullet)) continue; validHit = true; break; } if (validHit ||bullet.currentSpeed <= 0 || bullet.Velocity.sqrMagnitude <= 0.01f || bullet.ElapsedTime >= 8) { instances.TryRemove(bullet); //pool.Return(bullet.model); } else { // // 计算子弹的下坠距离 // float distance =Mathf.Clamp( // 0.5f * Physics.gravity.y * bullet.ElapsedTime * bullet.ElapsedTime, // Physics.gravity.y // ,0 // ) ; // // bullet.currentSpeed -= bullet.startSpeed * Time.fixedDeltaTime; // bullet.currentPos += (Vector3)bullet.forward * (bullet.currentSpeed * Time.fixedDeltaTime); // bullet.currentPos += Vector3.up * distance; // bullet.model.transform.position = bullet.currentPos; // // bullet.ElapsedTime += Time.fixedDeltaTime; // 计算重力下坠 bullet.ElapsedTime+=Time.fixedDeltaTime; bullet.Velocity+= Physics.gravity * (Time.fixedDeltaTime * bullet.ElapsedTime); // // 计算空气阻力 // float airDragForce = 0.5f * dragCoefficient * 1.225f * bullet.Velocity.magnitude * bullet.Velocity.magnitude; // // if (float.IsNaN(airDragForce)) // { // bullet.Velocity=Vector3.zero; // continue; // } // // Vector3 airDragDirection = -bullet.Velocity.normalized; // Vector3 airDrag = airDragDirection * airDragForce / bulletMass; var newVelocity =bullet.Velocity * Time.fixedDeltaTime; bullet.currentPos+=newVelocity; float CalculateElevationAngle(float distance) { float g = Mathf.Abs(Physics.gravity.y); float angle = 0.5f * Mathf.Asin(distance * g / (bullet.startSpeed * bullet.startSpeed)); return angle * Mathf.Rad2Deg; } } } } private async void RpcSpawnBullet(SpawnBullet x) { await UniTask.SwitchToMainThread(); InstanceBullet bullet = new() { initiator = x.initiator, currentPos = x.pos, startSpeed = x.startSpeed, pos = x.pos, rot = x.rot, forward = x.forward, initialDamage = x.initialDamage, InitialForce = x.InitialForce, Velocity =x.forward * x.startSpeed, }; instances.Add(bullet); } private void SpawnBullet(SpawnBullet x) { RpcSpawnBullet(x); } private bool IsValidHit(RaycastHit raycastHit, InstanceBullet bullet) { if (layerMask.Includes(raycastHit.collider.gameObject.layer) is false) return false; raycastHit.collider.TryGetComponentAny(out var physicsInfo); var force = (raycastHit.point - (Vector3)bullet.pos).normalized * (physicsInfo?.AddForceMultiple ?? 64); if (raycastHit.collider.TryGetComponent(out var damagable)) { if (damagable.UnityEntity?.Id == bullet.initiator) return false; if (layerMask.Includes(raycastHit.collider.gameObject.layer) is false) return false; var msg = new DamageMessage() { Target = damagable.UnityEntity, Hit = damagable, Damage = bullet.initialDamage, }; if (damagable is IEntityComponent) { damageService.Execute(msg); } else { damagable.GiveDamage(msg); } } var _rigidbody = (raycastHit.rigidbody,damagable?.Rigidbody) switch { (null, null) => null, (null, not null) => damagable?.Rigidbody, (not null, null) => raycastHit.rigidbody, (not null, not null) => damagable?.Rigidbody, }; if (_rigidbody is not null && _rigidbody.gameObject.layer is not 0) { _rigidbody.AddForceAtPositionAsync(force, raycastHit.point, ForceMode.Impulse).Forget(); } List tags = new() { "BulletHit", }; if (raycastHit.collider.TryGetComponent(out var _tag)) { tags.AddRange(_tag.GetTags()); } Location location = new() { position = raycastHit.point, forward = raycastHit.normal, }; var vfx = DI.Get().Spawn(location, tags.ToArray()); var constraint = vfx.gameObject.GetOrAddComponent(); var sourceTransform = raycastHit.transform; while (constraint.sourceCount>0) { constraint.RemoveSource(0); } constraint.AddSource(new ConstraintSource() { sourceTransform = sourceTransform, weight = 1, }); var positionOffset = sourceTransform.InverseTransformPoint(raycastHit.point); var rotationOffset = Quaternion.Inverse(sourceTransform.rotation) * transform.rotation; constraint.SetTranslationOffset(0, positionOffset); //constraint.SetRotationOffset(0, rotationOffset.eulerAngles); constraint.weight = 1; constraint.constraintActive = true; return true; } void IBulletService.Spawn(SpawnBullet bullet) => SpawnBullet(bullet); } }