Danil Iarosh e7b983b52d level 1-3 finished, bug fixes etc
- закончил структуру уровня 1-3
- пофиксил баги с анимациями
- добавил новый тип платформ: скрывающиеся
- изменил логику платформ
2025-12-02 16:58:31 +03:00

257 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using RR.Game2D.Core.Managers;
using UnityEngine;
namespace RR.Common.Controls
{
public class COMInput : InputSource
{
//Количество = цифровые + 2 датчика.
//На момент написания количество цифровых входов 7*N.
//Настроить количество пинов под свою настройку платы
private const int PIN_COUNT = 16;
private const char POSITIVE = '0';
private const char NEGATIVE = '1';
private bool[] buttonState = new bool[PIN_COUNT];
private bool[] buttonDown = new bool[PIN_COUNT];
private bool[] buttonUp = new bool[PIN_COUNT];
//Свойство для определения текущего игрока
//Должно возвращать 1 или 2
//Используется для получения состояния кнопок с пометкой ...CurrentPlayer
private int currentPlayer => 1;//ПЕРЕОПРЕДЕЛИТЕ ОПРЕДЕЛЕНИЕ ТЕКУЩЕГО ИГРОКА ПОД СВОЮ СИСТЕМУ.
private GameManager gm;//я получаю кто сейчас ходит через этого менеджера. Если у вас другое, допишите.
public override bool Initialize()
{
try
{
gm = GameManager.Instance;
//ОБЯЗАТЕЛЬНЫЙ БЛОК
ComWrapper.OnInputReceived.AddListener(UpdateInputState);
}
catch (Exception e)
{
GameManager.Logger.Log("COMInput", $"Что то в COMInput {e.ToString()}");
return false;
}
return true;
}
#region PUBLIC_API
//ЭТА ГРУППА МЕТОДОВ ДЛЯ ПОЛУЧЕНИЯ СОСТОЯНИЯ ЛЮБОЙ КНОПКИ ПО ПИНУ (enum ButtonPinMapping)
/// <summary>
/// Получает состояние конкретной кнопки по её физическому пину.
/// Возвращает true если кнопка в данный момент нажата.
/// </summary>
/// <param name="button">Пин кнопки на периферийной плате</param>
/// <returns>True если кнопка нажата в текущем кадре</returns>
public override bool GetButton(ButtonPinMapping button) => buttonState[(int)button];
/// <summary>
/// Проверяет, была ли кнопка нажата в начале текущего кадра.
/// Удерживает true до тех пор, пока кто то не проверит, затем сбрасывает.
/// </summary>
/// <param name="button">Пин кнопки на периферийной плате</param>
/// <returns>True если кнопка была нажата в этом кадре</returns>
public override bool GetButtonDown(ButtonPinMapping button) => ConsumeButtonEvent(buttonDown, button);
/// <summary>
/// Проверяет, была ли кнопка отпущена в начале текущего кадра.
/// Удерживает true до тех пор, пока кто то не проверит, затем сбрасывает.
/// </summary>
/// <param name="button">Пин кнопки на периферийной плате</param>
/// <returns>True если кнопка была отпущена в этом кадре</returns>
public override bool GetButtonUp(ButtonPinMapping button) => ConsumeButtonEvent(buttonUp, button);
/// <summary>
/// Получает состояние кнопки по числовому индексу пина напрямую из буфера состояний.
/// Обходит перечисление ButtonPinMapping для прямого доступа к пинам по индексу.
/// </summary>
/// <param name="pinIndex">Числовой индекс пина (0-15)</param>
/// <returns>true если кнопка нажата в текущий момент</returns>
/// <remarks>
/// Используется когда необходим прямой доступ к пинам без приведения к перечислению.
/// Подходит для сценариев где количество пинов может превышать размер перечисления.
/// </remarks>
public override bool GetButton(int pinIndex) => buttonState[pinIndex];
/// <summary>
/// Проверяет, была ли кнопка нажата в начале текущего кадра по числовому индексу пина.
/// Возвращает true только в первом кадре после нажатия и сбрасывает флаг события.
/// </summary>
/// <param name="pinIndex">Числовой индекс пина (0-15)</param>
/// <returns>true если кнопка была нажата в этом кадре</returns>
/// <remarks>
/// Метод "потребляет" событие - после чтения флаг сбрасывается.
/// Использует общий механизм ConsumeButtonEvent для обработки одноразовых событий.
/// </remarks>
public override bool GetButtonDown(int pinIndex) => ConsumeButtonEvent(buttonDown, pinIndex);
/// <summary>
/// Проверяет, была ли кнопка отпущена в начале текущего кадра по числовому индексу пина.
/// Возвращает true только в первом кадре после отпускания и сбрасывает флаг события.
/// </summary>
/// <param name="pinIndex">Числовой индекс пина (0-15)</param>
/// <returns>true если кнопка была отпущена в этом кадре</returns>
/// <remarks>
/// Метод "потребляет" событие - после чтения флаг сбрасывается.
/// Использует общий механизм ConsumeButtonEvent для обработки одноразовых событий.
/// </remarks>
public override bool GetButtonUp(int pinIndex) => ConsumeButtonEvent(buttonUp, pinIndex);
// ------------------------------------------------------------------------------------
//ЭТА ГРУППА МЕТОДОВ ПОЗВОЛЯЕТ УЗНАТЬ СОСТОЯНИЯ КНОПОК АКТИВНОГО ИГРОКА (проверка по enum PlayerAction)
//Служебные кнопки типа startOnePlayer, startTwoPlayers, secret, extra, hasPrize, prizeGiven здесь не проверяются
/// <summary>
/// Получает состояние действия для текущего игрока.
/// Возвращает true если действие активно (кнопка нажата) у текущего игрока.
/// </summary>
/// <param name="button">Действие игрока (up, down, left, right, action, extra)</param>
/// <returns>True если действие активно у текущего игрока</returns>
public override bool GetButtonCurrentPlayer(PlayerAction button)
=> GetButton(GetButtonForCurrentPlayer(button));
/// <summary>
/// Проверяет, было ли действие начато текущим игроком в этом кадре.
/// Возвращает true только в первом кадре после начала действия.
/// </summary>
/// <param name="button">Действие игрока</param>
/// <returns>True если действие было начато в этом кадре</returns>
public override bool GetButtonDownCurrentPlayer(PlayerAction button)
=> GetButtonDown(GetButtonForCurrentPlayer(button));
/// <summary>
/// Проверяет, было ли действие завершено текущим игроком в этом кадре.
/// Возвращает true только в первом кадре после завершения действия.
/// </summary>
/// <param name="button">Действие игрока</param>
/// <returns>True если действие было завершено в этом кадре</returns>
public override bool GetButtonUpCurrentPlayer(PlayerAction button)
=> GetButtonUp(GetButtonForCurrentPlayer(button));
// ------------------------------------------------------------------------------------
//ЭТА ГРУППА МЕТОДОВ ПОЗВОЛЯЕТ УЗНАТЬ СОСТОЯНИЯ КНОПОК ЛЮБОГО ИГРОКА (проверка по enum PlayerAction)
//Служебные кнопки типа startOnePlayer, startTwoPlayers, secret, extra, hasPrize, prizeGiven здесь не проверяются
/// <summary>
/// Получает состояние действия для любого из игроков.
/// Возвращает true если действие активно у первого ИЛИ второго игрока.
/// </summary>
/// <param name="button">Действие игрока</param>
/// <returns>True если действие активно у любого игрока</returns>
public override bool GetButtonAnyPlayer(PlayerAction button)
=> CheckBothPlayers(button, GetButton);
/// <summary>
/// Проверяет, было ли действие начато любым игроком в этом кадре.
/// Возвращает true если первый ИЛИ второй игрок начали действие в этом кадре.
/// </summary>
/// <param name="button">Действие игрока</param>
/// <returns>True если действие было начато любым игроком в этом кадре</returns>
public override bool GetButtonDownAnyPlayer(PlayerAction button)
=> CheckBothPlayers(button, GetButtonDown);
/// <summary>
/// Проверяет, было ли действие завершено любым игроком в этом кадре.
/// Возвращает true если первый ИЛИ второй игрок завершили действие в этом кадре.
/// </summary>
/// <param name="button">Действие игрока</param>
/// <returns>True если действие было завершено любым игроком в этом кадре</returns>
public override bool GetButtonUpAnyPlayer(PlayerAction button)
=> CheckBothPlayers(button, GetButtonUp);
#endregion
//==========ХЕЛПЕРЫ==========
// Проверка нажатия с обоих контроллеров игроков
private bool CheckBothPlayers(PlayerAction action, Func<ButtonPinMapping, bool> buttonFunc)
{
var buttons = _buttonByAction(action);
return buttonFunc(buttons.player1button) || buttonFunc(buttons.player2button);
}
// Проверка нажатия с контроллера текущего игрока
private ButtonPinMapping GetButtonForCurrentPlayer(PlayerAction button)
{
var buttons = _buttonByAction(button);
return currentPlayer == 1 ? buttons.player1button : buttons.player2button;
}
// Маппер из действий игрока => пины платы
private (ButtonPinMapping player1button, ButtonPinMapping player2button) _buttonByAction(PlayerAction playerAction) =>
playerAction switch
{
PlayerAction.up => (ButtonPinMapping.up1, ButtonPinMapping.up2),
PlayerAction.down => (ButtonPinMapping.down1, ButtonPinMapping.down2),
PlayerAction.left => (ButtonPinMapping.left1, ButtonPinMapping.left2),
PlayerAction.right => (ButtonPinMapping.right1, ButtonPinMapping.right2),
PlayerAction.action => (ButtonPinMapping.action1, ButtonPinMapping.action2),
PlayerAction.extra => (ButtonPinMapping.extra, ButtonPinMapping.extra), // одинаковая кнопка
_ => throw new ArgumentException($"Unknown player action: {playerAction}")
};
// Обновляет состояния инпута когда приходит новый ввод с платы. Дергается событием.
private void UpdateInputState(string input)
{
// Будем принимать в нативном формате by Pasha. 0 - нажато 1 - не нажато (чтобы преобразовывать циклом)
// В параметрприходит строка вида "1111011111011111"
for (int i = 0; i < input.Length; i++)
{
// Получаем новое состояние пина
bool newState = input[i] == POSITIVE;
// Состояние пина изменилось => пишем в буферы
if (newState != buttonState[i])
{
if (newState) // Пин был зажат
{
buttonDown[i] = true;
buttonUp[i] = false;
}
else // Пин был отжат
{
buttonDown[i] = false;
buttonUp[i] = true;
}
}
buttonState[i] = newState;
}
if (input[^1] == POSITIVE) PlayerPrefs.SetInt("PrizeGivenBuffer", 1); //пишем в буфер чтобы между кадрами и порядком вызова не проебать
// DebugWindow.Message("Инпуты: " + string.Join("", buttonState));//можно вырезать
}
// Потребление "Нажатия"/"Отжатия". Возвращает что там было и чистит флаг из буфера
private static bool ConsumeButtonEvent(bool[] buffer, ButtonPinMapping button)
{
bool result = buffer[(int)button];
buffer[(int)button] = false;
return result;
}
/// <summary>
/// Вспомогательный метод для обработки одноразовых событий кнопок по числовому индексу.
/// Возвращает текущее значение флага и сбрасывает его для предотвращения повторной обработки.
/// </summary>
/// <param name="buffer">Буфер событий (buttonDown или buttonUp)</param>
/// <param name="pinIndex">Числовой индекс пина</param>
/// <returns>Состояние флага события до сброса</returns>
private static bool ConsumeButtonEvent(bool[] buffer, int pinIndex)
{
bool result = buffer[pinIndex];
buffer[pinIndex] = false;
return result;
}
}
}