Untitled

 avatar
unknown
csharp
a month ago
6.1 kB
14
Indexable
using System;
using System.Linq;
using System.Runtime.Serialization;

[TypeUid(7008)]
public class GameCameraComponent : ExternInitComponent
{


    [Export] public NodePath targetPath;
    [IgnoreDataMember] public Spatial target;
    [Export] public Camera CameraObject => CameraSwitch.camera;

    [Export] public float smoothing = 5f;
    [Export] public float rotSmoothing = 3f;
    [Export] public float MoveSpeed = 3f;
    [Export] public float maxDistance = 9f;

    [Export] public NodePath defaultPositionPath;
    [IgnoreDataMember] public Spatial defaultPosition;

    [Export] public NodePath connectNodePath;
    [IgnoreDataMember] public Spatial connectNode;

    [Export] int GroundLayer;
    [Export] public bool Follow;

    [Export] public NodePath cameraRigidPath;
    [IgnoreDataMember] public RigidBody transform;

    public Node nodeOwner => (Node)this.ownerEntity.GetComponent<RealNodeOwnerComponent>().EntityNode;

    private CameraControlSwitchComponent cacheComponent;
    [IgnoreDataMember] public CameraControlSwitchComponent CameraSwitch
    {
        get
        {
            if(cacheComponent == null)
                cacheComponent = this.ownerEntity.GetComponent<CameraControlSwitchComponent>();
            return cacheComponent;
        }
    }

    // offset нам больше не нужен, мы будем использовать локальную позицию defaultPosition

    public override void ExternalInitialize()
    {
        base.ExternalInitialize();
        defaultPosition = nodeOwner.GetNode(defaultPositionPath) as Spatial;
        transform = nodeOwner.GetNode<RigidBody>(cameraRigidPath);
        connectNode = nodeOwner.GetNode<Spatial>(connectNodePath);

        GroundLayer = LayerMask.GetMask("ground");

        this.ownerEntity.GetComponent<RealNodeOwnerComponent>().EntityNode.Hooks.RegisterHook(
            GodotCommon.Starter.HookController.HookMethod.PhysicsProcess,
            0,
            FixedUpdate, this.instanceId
        );
    }

    protected override void OnAdded(ECSEntity entity)
    {
        base.OnAdded(entity);
        if(string.IsNullOrEmpty(targetPath))
        {
            var firstObj = this.ownerEntity.ECSWorldOwner.entityManager.SearchGraph(null, new Type[]{typeof(PhysicsComponent)}).ToHashSet().FirstOrDefault();
            if(firstObj!=null)
            {
                target = firstObj.GetComponent<PhysicsComponent>().Rigidbody;
            }
        }
        else
        {
            target = nodeOwner.GetNode(targetPath) as Spatial;
        }
    }

    public void FixedUpdate(float dt)
    {
        if (CameraSwitch.cameraMode == CameraControlSwitchComponent.CameraMode.Target && target != null)
        {
            if(CameraSwitch.camera.GetParent() != this.connectNode)
            {
                CameraSwitch.camera.ChangeParent(this.connectNode);
            }
            //float dt = TimeService.deltaTime;

            // 1. ПЛАВНО СЛЕДУЕМ ЗА ПОЗИЦИЕЙ
            transform.GlobalPosition = Vector3U.Lerp(transform.GlobalPosition, target.GlobalPosition, smoothing * dt);

            // 2. ПЛАВНО СЛЕДУЕМ ЗА ПОВОРОТОМ (Учитываем нормализацию из-за возможного масштаба)
            Quat currentRot = new Quat(transform.GlobalTransform.basis).Normalized();
            Quat targetRot = new Quat(target.GlobalTransform.basis).Normalized();
            Quat newRot = currentRot.Slerp(targetRot, rotSmoothing * dt).Normalized();

            Transform t = transform.GlobalTransform;
            t.basis = new Basis(newRot);
            transform.GlobalTransform = t;

            float LeftTurnInput = true ? Input.IsKeyPressed((int)KeyList.Q) ? 1f : 0f : 0f;
            float RightTurnInput = true ? Input.IsKeyPressed((int)KeyList.E) ? 1f : 0f : 0f;

            // Вычисляем изменение высоты
            float heightDirection = RightTurnInput - LeftTurnInput;
            if (Mathf.Abs(heightDirection) > 0.01f)
            {
                // Меняем ЛОКАЛЬНУЮ позицию, чтобы камера двигалась вверх/вниз относительно текущего наклона танка
                Vector3 localPos = defaultPosition.Translation;
                localPos.y += MoveSpeed * heightDirection * dt;
                defaultPosition.Translation = localPos;
            }

            // 4. ЛОГИКА СТОЛКНОВЕНИЙ И ПЕРЕМЕЩЕНИЕ КАМЕРЫ
            float distanceOffset = 0.5f; // Отступ от стены, чтобы камера не проваливалась в текстуры
            RaycastHit hit;

            Vector3 desiredGlobalPos = defaultPosition.GlobalPosition;

            if (Physics.Linecast(target.GlobalPosition, desiredGlobalPos, out hit, GroundLayer, QueryTriggerInteraction.Ignore))
            {
                // Вычисляем вектор от точки попадания к таргету, чтобы немного отдвинуть камеру от стены
                Vector3 dirToTarget = (target.GlobalPosition - hit.point).Normalized();
                Vector3 adjustedPos = hit.point + (dirToTarget * distanceOffset);

                CameraObject.GlobalPosition = Vector3U.Lerp(CameraObject.GlobalPosition, adjustedPos, smoothing * dt);
            }
            else
            {
                // Путь свободен, летим к идеальной позиции
                CameraObject.GlobalPosition = Vector3U.Lerp(CameraObject.GlobalPosition, desiredGlobalPos, smoothing * dt);
            }

            // 5. КАМЕРА ВСЕГДА СМОТРИТ НА ЦЕЛЬ
            CameraObject.LookAt(target.GlobalPosition, Vector3.Up);
        }
    }

    protected override void OnRemoved(ECSEntity entity)
    {
        base.OnRemoved(entity);
        this.ownerEntity.GetComponent<RealNodeOwnerComponent>().EntityNode.Hooks.Reset(this.instanceId);
    }
}
Editor is loading...