Оглавление
Анимация в консольном окне

Консольное окно прекрасный инструмент для тестирования программных алгоритмов и изучения языка программирования F#. Библиотека платформы .NET предоставляет удобную оболочку для работы с консольным окном. Консоль не требуется никаких элементов управления - написал программный код и запускай программу.
Тем не менее для консоли можно писать забавные программки и даже создавать живую анимацию текстовых символов. Один из способов программирования плавного движения символов описывается на этой странице. Данный анимационный полуфабрикат в дальнейшем можно использовать для создания небольших консольных игр.
Структура программы анимации символов
Исходный код разделён на три функциональных модуля F#:- Eraser - содержит два типа: запись Coordinates для хранения координат и класс Rectangle формирующий прямоугольную фигуру из символов.
- Game - обеспечивает анимацию движения прямоугольника символов.
- Program - отвечает за запуск и закрытие программы, предоставляет интерфейс взаимодействия с пользователем при помощи клавиатуры.
Модуль Eraser
type Coordinates = { mutable row: int; mutable col: int }
// Пример изменения координат:
let pos: Coordinates = { row = 0; col = 0 } // создание экземпляра записи
// Присвоение полям новых координат.
pos.row <- 12
pos.col <- 5
Прямоугольник из символов создаётся классом Rectangle. Конструктор класса принимает два параметра: число строк и столбцов. Эти размерности используются в классе для создания двумерного массива arrayPos. Двумерный массив для элементов типа Coordinates хранит координаты каждого символа в группе. В классе Rectangle имеется открытый метод Move direction для передвижения фигуры из символов на один шаг в указанном направлении.
Стирание символа основывается на печатании пробела по координатам символа. Алгоритм передвижения состоит из чередования циклов стирания части символов и рисования их по новым координатам:- - при вертикальных перемещениях стираются верхний или нижний ряд символов, затем изменяются на единицу координаты строк, далее рисуются символы в новом расположении по вертикали;
- - при горизонтальных перемещениях стираются первый или последний столбец символов, затем изменяются на единицу координаты столбов, далее рисуются символы в новом расположении по горизонтали.
Rectangle умеет передвигать символы только на один шаг. Свойства класса Pos, Right, Left, Top, Bottom выдают информацию о координатах символов, и также крайних рядов и столбцов для вычисления касания помех и целей. Создание непрерывной анимации не входит в функции класса.
type Rectangle(numrows: int, numbercols: int) =
// Одиночный символ группы.
let symbol = "●"
// Координаты символов по рядам и столбцам.
let arrayPos: Coordinates[,]=
Array2D.create numrows numbercols { row = 0; col = 0 }
// Установка позиция курсора для рисования символа
// в любом месте консольного окна.
let cursorpos pos =
Console.CursorLeft <- pos.col
Console.CursorTop <- pos.row
// Рисование символа в данной координате.
let drawsymbols pos =
cursorpos pos
printf "%s" symbol
// Стирание символа в данной координате.
let erasesymbols pos =
cursorpos pos
printf "%s" " "
// Стирание необходимых рядов и столбцов.
let erase direction =
match direction with
| 1 -> // erase right
for i = 0 to numrows - 1 do
let leftcol = arrayPos.[i, 0]
erasesymbols leftcol
| 2 -> // erase left
for i = 0 to numrows - 1 do
let rightcol = arrayPos.[i, numbercols - 1]
erasesymbols rightcol
| 3 -> // erase down
for i = 0 to numbercols - 1 do
let toprow = arrayPos.[0, i]
erasesymbols toprow
| 4 -> // erase up
for i = 0 to numbercols - 1 do
let bottomcol = arrayPos.[numrows - 1, i]
erasesymbols bottomcol
| _ -> ()
let lastIndex dimension = arrayPos.GetLength(dimension) - 1
// Инициализация прямоугольника символов.
do
for row = 0 to numrows - 1 do
for col = 0 to numbercols - 1 do
arrayPos.[row, col] <- { row = row; col = col }
drawsymbols arrayPos.[row, col]
// Получение координат прямоугольника.
member x.Pos = arrayPos
// Одномерные массивы крайних символов для каждой стороны.
// Крайние ряды и столбцы для проверки касания.
member x.Right = arrayPos[*, lastIndex(1)]
member x.Left = arrayPos[*, 0]
member x.Top = arrayPos[0, *]
member x.Bottom = arrayPos[lastIndex(0), *]
// Передвижение на один шаг в указанном направлении.
member x.Move direction =
let mutable x = 0
let mutable y = 0
// Определитель направления движения.
match direction with
// направо
| 1 when arrayPos.[0, lastIndex (1)].col < (Console.WindowWidth - 1) -> x <- 1
// налево
| 2 when arrayPos.[0, 0].col > 0 -> x <- -1
// вниз
| 3 when arrayPos.[lastIndex (0), 0].row < Console.WindowHeight -> y <- 1
// вверх
| 4 when arrayPos.[0, 0].row > 0 -> y <- -1
| _ -> ()
// Передвижение только если выбрано реальное направление.
if x <> 0 || y <> 0 then
erase direction
// Каждый символ передвигаем на шаг в указанном направлении.
// Функция iter применяется к каждому элементу массива.
arrayPos
|> Array2D.iter
(fun pos ->
pos.col <- pos.col + x
pos.row <- pos.row + y
drawsymbols pos)
Модуль создания анимации
Модуль содержит только один класс с одноименным названием. Первичный конструктор класса определяет скорость анимации и количество символов в прямоугольнике по рядам и столбцам. Класс Game обеспечивает непрерывную анимацию движения. Движок анимации построен на таймере, создающем события через определенные промежутки времени. Game владеет объектом класса Rectangle, описанным выше. Метод последнего Move direction непрерывно вызывается в событии таймера, таким образом создаётся анимационная иллюзия движения. Задавая различные интервалы срабатывания таймера можно изменять скорость движения символов.
После шага группы символов происходит проверка их координат в локальной функции checkPos (). В данном исходнике реализовано маятниковое движение "от стенки к стенке". Функция checkPos () получает актуальное положение символов и может использоваться для определения любого алгоритма реагирования касания помехи или цели.
Класс Game имеет открытые методы Right(), Left(), Down(), Up() для создания интерфейса взаимодействия с пользователем. Данные методы изменяют направление непрерывного движения соответственно их названию.
Программный код модуля Game:module Game
open System
open System.Timers
// В конструкторе определяется интервал кадров анимации,
// количество строк и столбов в группе символов.
type Game(interval, numrows, numcols) =
// Объект группы символов.
let eraser = Eraser.Rectangle(numrows, numcols)
// Таймер для создания анимации.
let timer =
new Timer(Interval = interval, Enabled = true)
// Указатель направления движения.
let mutable direction = 0
// Получение актуальных координат всех анимированных символов.
let checkPos () =
let eraserpos = eraser.Pos
// Достигли правой стенки, движение обратно к левой.
for i = 0 to eraser.Right.Length - 1 do
if eraser.Right[ i ].col = Console.WindowWidth - 1 then
direction <- 2
for i = 0 to eraser.Left.Length - 1 do
if eraser.Left[ i ].col = 0 then
direction <- 1
// Достигли верха переключаем на движение вниз.
for i = 0 to eraser.Top.Length - 1 do
if eraser.Top[ i ].row = 0 then
direction <- 3
for i = 0 to eraser.Bottom.Length - 1 do
if eraser.Bottom[ i ].row = Console.WindowHeight then
direction <- 4
// Функция события таймера.
let timerClick (source: Object) (e: System.Timers.ElapsedEventArgs) : unit =
// Перемещение на шаг.
eraser.Move direction
// Получение актуальных координат символов.
checkPos ()
// Для надёжного скрытия курсора.
// При изменении размеров окна курсор вновь появляется.
Console.CursorVisible <- false
// Добавление обработчика события таймера.
do timer.Elapsed.AddHandler(timerClick)
// Методы взаимодействия с пользователем.
member x.Right() = direction <- 1
member x.Left() = direction <- 2
member x.Down() = direction <- 3
member x.Up() = direction <- 4
Модуль запуска и закрытия программы
Модуль Program не имеет классов. Здесь выполняются предварительные настройки консольного окна. Далее создаётся объект класса Game с числовыми параметрами инициализации программы-игры. Затем запускается условно-бесконечный цикл прослушивания нажатых клавиш. Нажатия на клавиши-стрелки определяют направления движения символов, нажатие на пробел останавливает игру и закрывает окно программы.
Консольное приложение имеет неявную точку входа. В модуле нет функции main args с атрибутом [<EntryPoint>], что означает выполнение всех модулей программы по порядку. Такая особенность появилась в .NET6. Но при необходимости можно явно определить точку входа.
Программный код модуля Program:open System
open System.Text
// --- Предварительные настройки консольного окна ---
// Вывод символов кодировки UTF-8.
Console.OutputEncoding <- Encoding.UTF8
// Скрытие мигающего курсора.
Console.CursorVisible <- false
// Установка цвета фона и символов.
Console.BackgroundColor <- ConsoleColor.Blue
Console.ForegroundColor <- ConsoleColor.Black
// Обновление окна.
Console.Clear()
// Объект с определёнными параметрами игры:
// интервал таймера, количество строк и столбцов.
let game = Game.Game(100, 5, 10)
let mutable exit = ConsoleKey.A
// Условно-бесконечный цикл игры.
while exit <> ConsoleKey.Spacebar do
let action = Console.ReadKey(true)
if action.Key = ConsoleKey.RightArrow then
game.Right()
if action.Key = ConsoleKey.LeftArrow then
game.Left()
if action.Key = ConsoleKey.DownArrow then
game.Down()
if action.Key = ConsoleKey.UpArrow then
game.Up()
exit <- action.Key
Исходник программы
Исходник написан в среде программирования MS Visual Studio 2022, платформа .NET6. На основе исходника можно создавать небольшие программы-игры для консольного окна. Функция определения актуальной позиции символов класса Game позволяет создавать разнообразные по логике игры.
В архивном файле исходник и файл программы для ознакомления.
Скачать исходник
Eraser-vs17.zip
- Размер: 1102 Кбайт
- Загрузки: 95