/
karachi
/
KPO_CP_CrossyRoad
Обзор
Документация
Войти
/
karachi
/
KPO_CP_CrossyRoad
Код
Запросы
5
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
console
ViewConsole/Game/GameViewConsole.cs
361 строка
13 KB
karachi-23
console
11 янв 2025, 00:00
11 янв 2025, 00:00
3330cd2
Код
Авторство
О чём код?
using Microsoft.Win32.SafeHandles; using Model.Game; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; using System.Text; using System.Threading.Tasks; using View.Game; namespace ViewConsole.Game { /// <summary> /// Представление игры в консоли /// </summary> public class GameViewConsole : GameView { /// <summary> /// Ширина игрового окна консоли /// </summary> public const int GAME_CONSOLE_WIDTH = 80; /// <summary> /// Высота игрового окна консоли /// </summary> public const int GAME_CONSOLE_HEIGHT = 50; /// <summary> /// Длина буфера для консоли /// </summary> private const int CONSOLE_BUFFER_LENGTH = GAME_CONSOLE_HEIGHT * GAME_CONSOLE_WIDTH; /// <summary> /// Смещение для битового сдвига влево, чтобы настроить цвет фона консоли /// </summary> private const int BACKGROUND_COLOR = 4; /// <summary> /// Дескриптор вывода консоли /// </summary> private readonly SafeFileHandle _safeFileHandleConsole; /// <summary> /// Буфер для содержимого консоли /// </summary> private readonly ConsoleHelperUtilite.CharInfo[] _consoleBuffer; /// <summary> /// Игровое поле /// </summary> private readonly Map _map; /// <summary> /// Генератор случайных чисел /// </summary> private readonly Random _random = new(); /// <summary> /// Цвет игроков /// </summary> private readonly Dictionary<Player, ConsoleColor> _playerColor = new(); /// <summary> /// Цвет автомобилей /// </summary> private readonly Dictionary<Car, ConsoleColor> _carColor = new(); /// <summary> /// Флаг показывающий необходимость обновления состояния игры /// </summary> public bool AllowRender { get; set; } = false; /// <summary> /// Конструктор представления игры в консоли /// </summary> public GameViewConsole() { _safeFileHandleConsole = ConsoleHelperUtilite.GetConsoleOutputHandle(); _consoleBuffer = new ConsoleHelperUtilite.CharInfo[CONSOLE_BUFFER_LENGTH]; _map = GameInstance.Map; OnPlayerCreated(GameInstance.Map.Player); GameInstance.Map.Cars.ForEach(OnCarCreated); GameInstance.Map.PlayerCreated += OnPlayerCreated; GameInstance.Map.CarCreated += OnCarCreated; GameInstance.Map.CarDeleted += OnCarDeleted; GameInstance.CanRender += SetAllowRender; } /// <summary> /// Установка флага необходимости обновления состояния игры /// </summary> public void SetAllowRender() { AllowRender = true; } /// <summary> /// Обработка добавления игрока на поле /// </summary> /// <param name="parPlayer">Игрок</param> private void OnPlayerCreated(Player parPlayer) { _playerColor.Add(parPlayer, StaticProperties.PLAYER_COLOR); } /// <summary> /// Обработка добавления машин на поле /// </summary> /// <param name="parCar"></param> private void OnCarCreated(Car parCar) { _carColor.Add(parCar, StaticProperties.CAR_COLOR); } /// <summary> /// Обработка удаления машин с поля /// </summary> /// <param name="parCar"></param> /// <param name="_"></param> private void OnCarDeleted(Car parCar, Player? _) { _carColor.Remove(parCar); } /// <summary> /// Создание камеры /// </summary> /// <returns>Камера</returns> protected override Camera CreateCamera() { Camera camera = new CameraConsole(GameInstance) { ScreenWidth = GAME_CONSOLE_WIDTH, ScreenHeight = GAME_CONSOLE_HEIGHT, }; camera.UpdateScreenWidthToHeightRatio(); return camera; } /// <summary> /// Функция отрисовки /// </summary> public void Render() { if (AllowRender) { Camera.Update(); ClearConsoleBuffer(); DrawMap(); ConsoleHelperUtilite.PrintToConsoleFast(_safeFileHandleConsole, _consoleBuffer, GAME_CONSOLE_WIDTH, GAME_CONSOLE_HEIGHT); AllowRender = false; } } /// <summary> /// Очистка буфера консоли /// </summary> private void ClearConsoleBuffer() { Array.Fill(_consoleBuffer, new() { Char = new() { UnicodeChar = ' ', }, Attributes = (short)StaticProperties.GAME_BACKGROUND_COLOR << BACKGROUND_COLOR }); } /// <summary> /// Отрисовка карты /// </summary> protected override void DrawMap() { DrawMapBorder(); DrawPlayer(GameInstance.Map.Player); DrawCars(); DrawGameStatistic(); } /// <summary> /// Вывод символа в буфер консоли /// </summary> /// <param name="parX">Позиция по горизонтали</param> /// <param name="parY">Позиция по вертикали</param> /// <param name="parColor">Цвет</param> /// <param name="parChar">Символ</param> private void PlaceCharToBuffer(int parX, int parY, ConsoleColor parColor, char parChar) { if(parX < 0 || parX >= GAME_CONSOLE_WIDTH || parY < 0 || parY >= GAME_CONSOLE_HEIGHT) return; int offset = parY * GAME_CONSOLE_WIDTH + parX; _consoleBuffer[offset].Attributes = (short)(((short)parColor) | ((short)StaticProperties.GAME_BACKGROUND_COLOR) << BACKGROUND_COLOR); _consoleBuffer[offset].Char.UnicodeChar = parChar; } /// <summary> /// Отрисовка границ игры /// </summary> private void DrawMapBorder() { Camera camera = Camera; Vector2 leftUpGameFieldCornerOnScreen = camera.CalculatePointPositionInScreen(new(0, 0)); Vector2 rightUpGameFieldCornerOnScreen = camera.CalculatePointPositionInScreen(new(_map.Width, 0)); Vector2 leftDownGameFieldCornerOnScreen = camera.CalculatePointPositionInScreen(new(0, _map.Height)); Vector2 rightDownGameFieldCornerOnScreen = camera.CalculatePointPositionInScreen(new(_map.Width, _map.Height)); int xLeft = (int)leftUpGameFieldCornerOnScreen.X; int xRight = (int)rightUpGameFieldCornerOnScreen.X; int infiniteTopY = -10000; for (int y = infiniteTopY; y <= (int)leftDownGameFieldCornerOnScreen.Y; y++) { PlaceCharToBuffer(xLeft, y, StaticProperties.TEXT_COLOR, '\xB3'); PlaceCharToBuffer(xRight, y, StaticProperties.TEXT_COLOR, '\xB3'); } } /// <summary> /// Отрисовка игрока /// </summary> /// <param name="parPlayer">Игрок</param> /// <param name="parColor">Цвет</param> /// <param name="parChar">Символ</param> private void DrawPlayer(Player parPlayer, ConsoleColor parColor, char parChar) { Vector2 carSize = new(parPlayer.Width, parPlayer.Length); Vector2 topVertex = parPlayer.Position + new Vector2(0, -carSize.Y / 2); Vector2 leftVertex = parPlayer.Position + new Vector2(-carSize.X / 2, carSize.Y / 2); Vector2 rightVertex = parPlayer.Position + new Vector2(carSize.X / 2, carSize.Y / 2); Vector2 topOnScreen = Camera.CalculatePointPositionInScreen(topVertex); Vector2 leftOnScreen = Camera.CalculatePointPositionInScreen(leftVertex); Vector2 rightOnScreen = Camera.CalculatePointPositionInScreen(rightVertex); DrawLine((int)topOnScreen.X, (int)topOnScreen.Y, (int)leftOnScreen.X, (int)leftOnScreen.Y, parColor, parChar); DrawLine((int)topOnScreen.X, (int)topOnScreen.Y, (int)rightOnScreen.X, (int)rightOnScreen.Y, parColor, parChar); DrawLine((int)leftOnScreen.X, (int)leftOnScreen.Y, (int)rightOnScreen.X, (int)rightOnScreen.Y, parColor, parChar); } /// <summary> /// Отрисовка линии /// </summary> /// <param name="x1">Начальная позиция линии по оси Х</param> /// <param name="y1">Начальная позиция линии по оси Y</param> /// <param name="x2">Конечная позиция линии по оси X</param> /// <param name="y2">Конечная позиция линии по оси Y</param> /// <param name="parColor">Цвет</param> /// <param name="parChar">Символ</param> private void DrawLine(int x1, int y1, int x2, int y2, ConsoleColor parColor, char parChar) { int dx = Math.Abs(x2 - x1), sx = x1 < x2 ? 1 : -1; int dy = -Math.Abs(y2 - y1), sy = y1 < y2 ? 1 : -1; int err = dx + dy, e2; while (true) { PlaceCharToBuffer(x1, y1, parColor, parChar); if (x1 == x2 && y1 == y2) break; e2 = 2 * err; if (e2 >= dy) { err += dy; x1 += sx; } if (e2 <= dx) { err += dx; y1 += sy; } } } /// <summary> /// Отрисовка игрока /// </summary> /// <param name="parPlayer">Игрок</param> private void DrawPlayer(Player parPlayer) { ConsoleColor cellColor = StaticProperties.PLAYER_COLOR; DrawPlayer(parPlayer, cellColor, '$'); Player player = parPlayer; Vector2 playerTextPosition = Camera.CalculatePointPositionInScreen(player.Position); float halfWidthOnScreen = Camera.CalculateLineLengthInScreen(player.Width) / 2; float halfLengthOnScreen = Camera.CalculateLineLengthInScreen(player.Length) / 2; string label = $"{parPlayer.Name}"; int x = (int)(playerTextPosition.X - halfWidthOnScreen); int y = (int)(playerTextPosition.Y - halfLengthOnScreen); foreach (char elChar in label) PlaceCharToBuffer(x++, y, StaticProperties.TEXT_COLOR, elChar); } /// <summary> /// Отрисовка машины /// </summary> /// <param name="parCar">Машина</param> /// <param name="parColor">Цвет</param> /// <param name="parChar">Символ</param> private void DrawCar(Car parCar, ConsoleColor parColor, char parChar) { Vector2 carSize = new(parCar.Width, parCar.Length); Vector2 carLeftUp = parCar.Position - carSize / 2; Vector2 carRightDown = carLeftUp + carSize; Vector2 leftUpOnScreen = Camera.CalculatePointPositionInScreen(carLeftUp); Vector2 rightDownOnScreen = Camera.CalculatePointPositionInScreen(carRightDown); for (int x = (int)leftUpOnScreen.X; x <= rightDownOnScreen.X; x++) { PlaceCharToBuffer(x, (int)leftUpOnScreen.Y, parColor, parChar); PlaceCharToBuffer(x, (int)rightDownOnScreen.Y, parColor, parChar); } for (int y = (int)leftUpOnScreen.Y; y <= rightDownOnScreen.Y; y++) { PlaceCharToBuffer((int)leftUpOnScreen.X, y, parColor, parChar); PlaceCharToBuffer((int)rightDownOnScreen.X, y, parColor, parChar); } } /// <summary> /// Отрисовка всех машин на карте /// </summary> private void DrawCars() { List<Car> carInViewport = Camera.GetCarsInViewport(); foreach (Car elCar in carInViewport) DrawCar(elCar, StaticProperties.CAR_COLOR, '$'); } /// <summary> /// Выводит строку /// </summary> /// <param name="parString">Строка для вывода</param> /// <param name="parColor">Цвет</param> /// <param name="parX">Начальная позиция по координате X</param> /// <param name="parY">Начальная позиция по координате Y</param> private void PrintString(string parString, ConsoleColor parColor, int parX, int parY) { int l = parString.Length; for (int i = 0; i < l; ++i) PlaceCharToBuffer(parX++, parY, parColor, parString[i]); } /// <summary> /// Отображение статистики в консоли /// </summary> protected override void DrawGameStatistic() { Map map = GameInstance.Map; int x = 0; int y = 0; PrintString($"Score: {(int)map.Player.MaxScore}", StaticProperties.TEXT_COLOR, x, ++y); } /// <summary> /// Настройка экрана при запуске игры /// </summary> public override void OnStartGame() { if (OperatingSystem.IsWindows()) { Console.WindowHeight = GAME_CONSOLE_HEIGHT; Console.WindowWidth = GAME_CONSOLE_WIDTH; Console.BufferHeight = GAME_CONSOLE_HEIGHT; Console.BufferWidth = GAME_CONSOLE_WIDTH; } Console.BackgroundColor = StaticProperties.GAME_BACKGROUND_COLOR; Console.ForegroundColor = StaticProperties.TEXT_COLOR; } /// <summary> /// Обновление представления при установке паузы /// </summary> protected override void OnPause() { Debug.WriteLine("Pause"); } /// <summary> /// Обновление представления при возобновлении игры /// </summary> protected override void OnResume() { Debug.WriteLine("Resume"); } /// <summary> /// Обновление представления при остановке игры /// </summary> protected override void OnStopGame() { Debug.WriteLine("Stop game"); } } }