Подробные изменения: - Добавлены и доработаны модальные окна главного меню, сервисные настройки, шаблоны UI Toolkit и единый scene-flow сервис для переходов между сценами. - Расширена gameplay-архитектура: сессия забега, HUD, спавн контента платформ, collectible-предметы, оборудование, хрупкие и движущиеся платформы. - Добавлена база противников: типы спавна, патрулирование, процедурный спавнер, правила размещения через ScriptableObject-сложности и убийство врагов прыжком сверху. - Исправлены смерть и respawn игрока, финальный GameOver, отскоки от врагов, пружин и boost-предметов, а также проход сквозь врагов при усиленном подъеме. - Проведен рефакторинг взаимодействия систем: общий landing context, единые правила player-contact, NonLazy gameplay session, fail-fast installer и централизованный выбор точек спавна. - Обновлены сцены, prefab-ассеты, difficulty stages, ресурсы главного меню, настройки проекта, roadmap и аппаратная COM-интеграция. - Добавлены ассеты летучей мыши, врагов, collectibles, TextMesh Pro, ProjectContext и gameplay UI. Проверки: - dotnet build .\\Assembly-CSharp.csproj --no-restore - Unity validation ключевых gameplay/UI/scene-flow скриптов - Unity Console после force refresh без ошибок
219 lines
9.6 KiB
C#
219 lines
9.6 KiB
C#
using System;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
|
||
namespace DanroJump.Hardware.Com
|
||
{
|
||
/// <summary>
|
||
/// Создает COM-транспорт через reflection-обертку над System.IO.Ports.SerialPort.
|
||
/// </summary>
|
||
public sealed class ReflectionSerialComTransportFactory : IComTransportFactory
|
||
{
|
||
/// <summary>
|
||
/// Создает транспорт для указанного serial-порта.
|
||
/// </summary>
|
||
public IComTransport Create(SerialSettings settings)
|
||
{
|
||
return ReflectionSerialComTransport.Create(settings);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Reflection-адаптер serial-порта, который изолирует проект от прямой ссылки на System.IO.Ports.
|
||
/// </summary>
|
||
internal sealed class ReflectionSerialComTransport : IComTransport
|
||
{
|
||
private const BindingFlags InstancePublicFlags = BindingFlags.Public | BindingFlags.Instance;
|
||
|
||
private static readonly Type SerialPortType = Type.GetType("System.IO.Ports.SerialPort, System");
|
||
private static readonly Type ParityType = Type.GetType("System.IO.Ports.Parity, System");
|
||
private static readonly Type StopBitsType = Type.GetType("System.IO.Ports.StopBits, System");
|
||
private static readonly PropertyInfo IsOpenProperty = SerialPortType?.GetProperty("IsOpen", InstancePublicFlags);
|
||
private static readonly PropertyInfo DataBitsProperty = SerialPortType?.GetProperty("DataBits", InstancePublicFlags);
|
||
private static readonly PropertyInfo ParityProperty = SerialPortType?.GetProperty("Parity", InstancePublicFlags);
|
||
private static readonly PropertyInfo StopBitsProperty = SerialPortType?.GetProperty("StopBits", InstancePublicFlags);
|
||
private static readonly PropertyInfo ReadTimeoutProperty = SerialPortType?.GetProperty("ReadTimeout", InstancePublicFlags);
|
||
private static readonly PropertyInfo WriteTimeoutProperty = SerialPortType?.GetProperty("WriteTimeout", InstancePublicFlags);
|
||
private static readonly PropertyInfo NewLineProperty = SerialPortType?.GetProperty("NewLine", InstancePublicFlags);
|
||
private static readonly PropertyInfo EncodingProperty = SerialPortType?.GetProperty("Encoding", InstancePublicFlags);
|
||
private static readonly PropertyInfo DtrEnableProperty = SerialPortType?.GetProperty("DtrEnable", InstancePublicFlags);
|
||
private static readonly PropertyInfo RtsEnableProperty = SerialPortType?.GetProperty("RtsEnable", InstancePublicFlags);
|
||
private static readonly MethodInfo OpenMethod = SerialPortType?.GetMethod("Open", InstancePublicFlags, null, Type.EmptyTypes, null);
|
||
private static readonly MethodInfo CloseMethod = SerialPortType?.GetMethod("Close", InstancePublicFlags, null, Type.EmptyTypes, null);
|
||
private static readonly MethodInfo DiscardInBufferMethod = SerialPortType?.GetMethod("DiscardInBuffer", InstancePublicFlags, null, Type.EmptyTypes, null);
|
||
private static readonly MethodInfo DiscardOutBufferMethod = SerialPortType?.GetMethod("DiscardOutBuffer", InstancePublicFlags, null, Type.EmptyTypes, null);
|
||
private static readonly MethodInfo ReadLineMethod = SerialPortType?.GetMethod("ReadLine", InstancePublicFlags, null, Type.EmptyTypes, null);
|
||
private static readonly MethodInfo WriteLineMethod = SerialPortType?.GetMethod("WriteLine", InstancePublicFlags, null, new[] { typeof(string) }, null);
|
||
|
||
private readonly object instance;
|
||
|
||
private ReflectionSerialComTransport(object instance)
|
||
{
|
||
this.instance = instance;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Показывает, открыт ли underlying serial-порт.
|
||
/// </summary>
|
||
public bool IsOpen => GetBoolProperty(IsOpenProperty);
|
||
|
||
/// <summary>
|
||
/// Создает и настраивает экземпляр SerialPort через reflection.
|
||
/// </summary>
|
||
public static ReflectionSerialComTransport Create(SerialSettings settings)
|
||
{
|
||
if (SerialPortType == null)
|
||
{
|
||
throw new InvalidOperationException("System.IO.Ports.SerialPort is not available.");
|
||
}
|
||
|
||
EnsureRequiredMetadata();
|
||
|
||
object serialPort = Activator.CreateInstance(SerialPortType, settings.PortName, settings.BaudRate);
|
||
var transport = new ReflectionSerialComTransport(serialPort);
|
||
transport.SetProperty(DataBitsProperty, settings.DataBits);
|
||
transport.SetProperty(ParityProperty, ParseEnum(ParityType, settings.Parity.ToString()));
|
||
transport.SetProperty(StopBitsProperty, ParseEnum(StopBitsType, settings.StopBits.ToString()));
|
||
transport.SetProperty(ReadTimeoutProperty, settings.ReadTimeoutMs);
|
||
transport.SetProperty(WriteTimeoutProperty, settings.WriteTimeoutMs);
|
||
transport.SetProperty(NewLineProperty, "\n");
|
||
transport.SetProperty(EncodingProperty, Encoding.UTF8);
|
||
transport.SetProperty(DtrEnableProperty, true);
|
||
transport.SetProperty(RtsEnableProperty, true);
|
||
return transport;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Открывает serial-порт.
|
||
/// </summary>
|
||
public void Open()
|
||
{
|
||
Invoke(OpenMethod);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Закрывает serial-порт.
|
||
/// </summary>
|
||
public void Close()
|
||
{
|
||
Invoke(CloseMethod);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очищает входной буфер serial-порта.
|
||
/// </summary>
|
||
public void DiscardInBuffer()
|
||
{
|
||
Invoke(DiscardInBufferMethod);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очищает выходной буфер serial-порта.
|
||
/// </summary>
|
||
public void DiscardOutBuffer()
|
||
{
|
||
Invoke(DiscardOutBufferMethod);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Читает одну строку из serial-порта.
|
||
/// </summary>
|
||
public string ReadLine()
|
||
{
|
||
return (string)Invoke(ReadLineMethod);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Записывает одну строку в serial-порт.
|
||
/// </summary>
|
||
public void WriteLine(string value)
|
||
{
|
||
Invoke(WriteLineMethod, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Освобождает underlying SerialPort, если он поддерживает IDisposable.
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
if (instance is IDisposable disposable)
|
||
{
|
||
disposable.Dispose();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверяет наличие всех reflection-членов заранее, чтобы ошибки были явными.
|
||
/// </summary>
|
||
private static void EnsureRequiredMetadata()
|
||
{
|
||
Require(DataBitsProperty, "DataBits");
|
||
Require(ParityProperty, "Parity");
|
||
Require(StopBitsProperty, "StopBits");
|
||
Require(ReadTimeoutProperty, "ReadTimeout");
|
||
Require(WriteTimeoutProperty, "WriteTimeout");
|
||
Require(NewLineProperty, "NewLine");
|
||
Require(EncodingProperty, "Encoding");
|
||
Require(DtrEnableProperty, "DtrEnable");
|
||
Require(RtsEnableProperty, "RtsEnable");
|
||
Require(IsOpenProperty, "IsOpen");
|
||
Require(OpenMethod, "Open()");
|
||
Require(CloseMethod, "Close()");
|
||
Require(DiscardInBufferMethod, "DiscardInBuffer()");
|
||
Require(DiscardOutBufferMethod, "DiscardOutBuffer()");
|
||
Require(ReadLineMethod, "ReadLine()");
|
||
Require(WriteLineMethod, "WriteLine(string)");
|
||
}
|
||
|
||
private static void Require(MemberInfo member, string memberName)
|
||
{
|
||
if (member == null)
|
||
{
|
||
throw new MissingMemberException(SerialPortType?.FullName ?? "System.IO.Ports.SerialPort", memberName);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Читает bool-свойство SerialPort через заранее найденный PropertyInfo.
|
||
/// </summary>
|
||
private bool GetBoolProperty(PropertyInfo property)
|
||
{
|
||
return property.GetValue(instance) is bool value && value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Записывает свойство SerialPort через заранее найденный PropertyInfo.
|
||
/// </summary>
|
||
private void SetProperty(PropertyInfo property, object value)
|
||
{
|
||
property.SetValue(instance, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Вызывает метод SerialPort через reflection и пробрасывает реальную inner-ошибку.
|
||
/// </summary>
|
||
private object Invoke(MethodInfo method, params object[] args)
|
||
{
|
||
try
|
||
{
|
||
return method.Invoke(instance, args);
|
||
}
|
||
catch (TargetInvocationException exception) when (exception.InnerException != null)
|
||
{
|
||
// Наружу пробрасываем реальную ошибку serial-порта, а не reflection-обертку.
|
||
throw exception.InnerException;
|
||
}
|
||
}
|
||
|
||
private static object ParseEnum(Type enumType, string value)
|
||
{
|
||
if (enumType == null)
|
||
{
|
||
throw new InvalidOperationException("System.IO.Ports enum metadata is not available.");
|
||
}
|
||
|
||
return Enum.Parse(enumType, value);
|
||
}
|
||
}
|
||
}
|