canto/Assets/Scripts/Entity/AimFeature.cs
2025-08-16 16:17:16 -04:00

110 lines
2.4 KiB
C#

using System;
using KitsuneCafe.Extension;
using KitsuneCafe.Input;
using KitsuneCafe.ItemSystem;
using KitsuneCafe.SOAP;
using R3;
using UnityEngine;
namespace KitsuneCafe.Entities
{
public class AimFeature : MonoBehaviour
{
[SerializeField]
private new Camera camera;
[SerializeField]
private EquipmentInstance equipment;
[SerializeField]
private Motor motor;
[SerializeField]
private ControlSchemeValue controlScheme;
[SerializeField]
private Vector2Value lookValue;
private void OnValidate()
{
if (camera == null)
{
camera = Camera.main;
}
if (equipment == null)
{
equipment = GetComponent<EquipmentInstance>();
}
if (motor == null)
{
motor = GetComponent<Motor>();
}
}
private void Start()
{
var d = Disposable.CreateBuilder();
var readied = Observable.FromEventHandler<bool>(
e => equipment.Readied += e,
e => equipment.Readied -= e
)
.Select(static eh => eh.e);
readied
.Subscribe(isReady =>
{
motor.enabled = !isReady;
})
.AddTo(ref d);
readied
.WhereTrue()
.SelectMany(_ =>
controlScheme.AsObservable()
.CombineLatest(
Observable.IntervalFrame(1),
static (scheme, _) => scheme
).TakeUntil(readied.WhereFalse())
)
.Subscribe(Aim)
.AddTo(ref d);
d.RegisterTo(destroyCancellationToken);
}
private void Aim(ControlScheme scheme)
{
switch (scheme)
{
case ControlScheme.Gamepad:
LookInDirection(lookValue.Value);
break;
default:
LookAtPosition(lookValue.Value);
break;
}
}
private void LookInDirection(Vector2 value)
{
transform.LookAt(transform.position + new Vector3(value.x, 0, value.y));
}
private void LookAtPosition(Vector2 value)
{
var screenPosition = camera.WorldToViewportPoint(transform.position);
var mouseOnScreen = camera.ScreenToViewportPoint(value);
float angle = AngleBetween(screenPosition, mouseOnScreen);
transform.rotation = Quaternion.Euler(new Vector3(0, -angle + 90, 0));
}
static float AngleBetween(Vector3 a, Vector3 b)
{
return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
}
}
}