LXF163: Изучаем языки
Olkol (обсуждение | вклад) (Новая страница: «Категория: Учебники Категория: Программирование '''Программирование: созд…») |
Olkol (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
[[Категория: Учебники]] | [[Категория: Учебники]] | ||
[[Категория: Программирование]] | [[Категория: Программирование]] | ||
− | |||
− | |||
'''Программирование: создаем классную эмуляцию звездного неба на трех разных языках''' | '''Программирование: создаем классную эмуляцию звездного неба на трех разных языках''' | ||
− | |||
== Изучаем языки == | == Изучаем языки == | ||
''Майк Сондерс вихрем промчит вас по трем языкам программирования и инструментариям, создавая на каждом эффект неба в звездах.'' | ''Майк Сондерс вихрем промчит вас по трем языкам программирования и инструментариям, создавая на каждом эффект неба в звездах.'' | ||
− | |||
− | |||
[[Файл: LXF160.tut shelr.expert.jpeg.png|left |100px |thumb|'''Наш эксперт'''. Поднаторев в языках от AREXX до ассемблера Z80, Майк Сондерс более всего чувствует себя дома с копией ассемблера NASM и исходным кодом своей ОС – http://mikeos.berlios.de.]] | [[Файл: LXF160.tut shelr.expert.jpeg.png|left |100px |thumb|'''Наш эксперт'''. Поднаторев в языках от AREXX до ассемблера Z80, Майк Сондерс более всего чувствует себя дома с копией ассемблера NASM и исходным кодом своей ОС – http://mikeos.berlios.de.]] | ||
Версия 03:53, 21 октября 2018
|
|
|
Программирование: создаем классную эмуляцию звездного неба на трех разных языках
Изучаем языки
Майк Сондерс вихрем промчит вас по трем языкам программирования и инструментариям, создавая на каждом эффект неба в звездах.
Вот в чем вопрос: сколько языков программирования нужно знать, чтобы быть действительно хорошим программистом? Точного ответа нет, но мы бы сказали, три-четыре. Конечно, можно выучить один от корки до корки и стать в нем настоящим гением, но ни один язык не совершенен, поэтому каких-то возможностей и идей в нем все равно не будет. Это похоже на обычные языки: даже если вы в совершенстве владеете английским, вы все равно можете почерпнуть очень многое из родственных ему языков, например, немецкого или французского. Вы научитесь не только заменять слова – вы научитесь мыслить иначе и попутно откроете для себя новую культуру.
Итак, действительно хороший программист обычно знает несколько языков программирования. И мы советуем всем, даже тем, у кого это лишь хобби, иногда пробовать и другие языки, ведь из каждого можно узнать что-то новое. Например, стоит познакомиться с языком низкого уровня, и вы узнаете много нового об управлении памятью и указателях, и сможете проще реализовывать сложные алгоритмы на языках высокого уровня. Зная несколько языков, вы всегда сможете выбрать верный для решения конкретной задачи (слишком многие хорошо знают только один язык, например, C++, и поэтому пытаются решить с его помощью все задачи).
Поэтому мы решили посвятить эту статью трем различным языкам. Но фишка в том, что на каждом из них мы будем делать одно и то же. Оно может показаться бессмысленным, но на самом деле это отличный способ увидеть каждый из них в действии и узнать, как одна и те же цель достигается с их разными подходами. Мы не будем давать длинных извилистых описаний языков: лучшее, что вы сможете сделать – прочитать код и наши краткие объяснения и начать разбираться в них самостоятельно. Итак, без долгих предисловий...
Низкий уровень: C и SDL
Мы напишем эмуляцию параллактического звездного неба с множеством звезд, движущихся по экрану с различными скоростями. Из нее можно сделать скрин-сейвер или фон для игры shoot’em-up [«перестреляй их всех» – жанр видео- и компьютерных игр, – прим. пер.]. Главное – эта задача поможет вам понять, как создавать массивы, выполнять вычисления, пользоваться циклами, рисовать пиксели и т. д.
Начнем с довольно низкого уровня на примере C и SDL. C считается многими «портируемым языком ассемблера» и не включает комфорта языков высокого уровня, наподобие сбора мусора. На C вы более тесно взаимодействуете с памятью и устройствами, и он идеально подходит для определенных задач. SDL (Simple DirectMedia Layer – простой уровень для доступа к мультимедиа) – очень популярная библиотека для работы с мультимедиа, которая позволяет работать с изображениями, шрифтами и звуком. Это не самая простая библиотека в сравнении с инструментарием для разработки игр, но, как и C, она дает вам мощный контроль над происходящим.
Итак, начнем с этой сладкой парочки и рассмотрим «сырой» способ создания звездного неба, а затем перейдем к высокоуровневым альтернативам. Ниже приведен код – его вы найдете на диске в файле starfield.c (откройте каталог c_with_sdl внутри starfield.tgz).
- include <stdlib.h>
- include <SDL.h>
- define MAX_STARS 100
typedef struct
{
int x, y, speed;
} star_type;
star_type stars[MAX_STARS];
int main()
{
int i;
Uint8 *p;
SDL_Surface *screen;
SDL_Init(SDL_INIT_VIDEO);
atexit(SDL_Quit);
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
for(i = 0; i < MAX_STARS; i++) {
stars[i].x = rand()%640;
stars[i].y = rand()%480;
stars[i].speed = 1 + rand()%16;
}
for(i = 0; i < SDL_NUMEVENTS; i++) {
if (i != SDL_QUIT)
SDL_EventState(i, SDL_IGNORE);
}
while (SDL_PollEvent(NULL) == 0) {
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
for(i = 0; i < MAX_STARS; i++) {
stars[i].x -= stars[i].speed;
if(stars[i].x <= 0)
stars[i].x = 640;
p = (Uint8 *) screen->pixels + stars[i].y * screen->pitch + stars[i].x * screen->format->BytesPerPixel;
- p = 255;
}
SDL_UpdateRect(screen, 0, 0, 0, 0);
SDL_Delay(30);
}
return 0;
}
Неплохо, а? Чуть больше 50 строк кода – и мы получили яркое звездное небо, все на чистом C и SDL на низком уровне. Для компиляции исполняемого файла выполните команду
gcc -o starfield starfield.c `sdl-config --cflags` `sdl-config --libs`
Обратите внимание на символы обратного апострофа – соответствующая клавиша скорее всего будет в левом верхнем углу клавиатуры. В общем случае все, что внутри обратных апострофов, считается командой, но эти команды выполняются до выполнения основной команды (gcc). Так, набрав sdl-config --libs, вы увидите параметры компилятора, необходимые для сборки программ SDL. Для запуска скомпилированной программы наберите ./starfield.
Ладно, взглянем-ка на код. Первые две строки говорят компилятору, что мы хотим подключить заголовочные файлы стандартной библиотеки (для генерации случайных чисел) и SDL (чтобы воспользоваться процедурами этой библиотеки). Затем следует строка #define, которая говорит GCC, что все последующие вхождения MAX_STARS в исходном коде нужно заменить числом 100. Это позволяет нам экспериментировать с количеством звезд, меняя всего одну строку в коде.
Далее мы определяем структуру – т. е. набор переменных, к которым можно обращаться вместе под одним именем. Мы назвали ее star_type, и каждая переменная этого типа будет иметь внутри переменные координат X и Y, а также скорости speed (все это целые числа). Строкой
star_type stars[MAX_STARS];
мы создаем новый массив структур star_type, который называется stars. В коде на диске это массив из 100 звезд, к элементам которого можно обращаться как к star[0] – star[99].
Песня о главном
Пора перейти к самому коду – внутрь функции main() (это первая функция, которая выполняется в программе на C – а в нашем коде это вообще единственная функция). Мы объявляем целочисленную переменную i, которая будет использоваться в качестве счетчика. Затем мы объявляем переменную p – беззнаковый восьмибитный целочисленный указатель; это переменная, которая затем будет ссылаться на графические данные. Наконец, у нас есть переменная screen – указатель, который будет использован в SDL.
Если вы никогда не слышали об указателях, они похожи на переменные, но хранят не числа, а адреса в памяти других переменных. Так, у вас может быть целочисленная переменная x, которая содержит число 50 и находится по адресу 1000 в оперативной памяти. Если создать указатель y и направить его на x, то y будет содержать не 50, а адрес в памяти – 1000. Работа с указателями весьма кропотлива, и многие языки программирования высокого уровня их избегают, но знать о них стоит. Затем следуют три строки, где инициализируются подсистемы SDL, сообщается компилятору, что мы хотим вызвать процедуру SDL_Quit по завершении программы, и создается новое окно (640 пикселей в ширину и 480 в высоту, в 8-битном режиме цвета для 256 цветов).
Вызов SDL_SetVideoMode возвращает структуру с выводимой информацией, поэтому мы направляем указатель screen на нее.
Далее идут два цикла for. Первый заполняет массив со звездами, записывая в координаты X и Y и скорость каждого элемента случайные значения. Попробуйте изменить 16 в строке со скоростью на большее и меньшее значения и посмотрите на результат. Второй цикл for велит SDL не докучать нам в ответ на каждое нажатие клавиши или движение мыши, которые она получает – она должна игнорировать все, кроме закрытия окна.
Затем мы переходим к циклу while, где и творится вся потеха. Мы говорим SDL, что хотим выполнять этот цикл до тех пор, пока окно не будет закрыто, и наше первое действие – заполнить экран черным цветом (RGB 0, 0, 0) с помощью функции SDL_FillRect (благодаря NULL заполняется все окно). Затем мы проходимся по массиву star, обновляя горизонтальное положение каждой звезды, вычитая ее скорость (точнее, перемещение за единицу времени) из ее координаты x. Пиксели окна нумеруются от 0 до 639 слева направо и от 0 до 479 сверху вниз. Если звезда вылетает за левую границу окна (т. е. ее координата x становится меньше нуля), мы перемещаем ее вправо, снова начиная с 640.
В двух строках, начинающихся с p и *p, звезды добавляются на «небо». В первой мы записываем в указатель p точное расположение графических данных, в которые нужно разместить пиксель. Эти данные хранятся в поле pixels структуры screen, и так как это линейный одномерный массив байтов, нужно выполнить кое-какие вычисления, чтобы оно соответствовало нашему двумерному изображению. Когда p указывает на верное место, мы записываем число 255 (белый цвет) в байт, на который он указывает (для этого используется звездочка).
Наконец, мы говорим SDL, что закончили отрисовку и нужно вывести все на экран, и добавляем задержку в 30 миллисекунд, чтобы звезды не двигались слишком быстро. Вот и все!
Если вы никогда не писали на C, программа могла показаться сложноватой, поэтому перейдем к языкам более высокого уровня. Освоив их, вернитесь сюда, и все станет яснее.
Высокий уровень: Python и Pygame
По сравнению с тем, что мы только что сделали, в Python с его библиотекой Pygame жизнь намного легче. Гораздо проще работать с графикой, и не нужно возиться с указателями. Ниже приведен код – это файл starfield.py на LXFDVD, и если у вас установлена Pygame, его можно запустить командой ./starfield.py. Если вы новичок в Python, код все равно будет достаточно понятным, но не забывайте обращать внимание на отступы, которые жизненно важны для работы программы. Код, выделяемый в блоки (например, в циклы), всегда нужно выделять отступами.
- !/usr/bin/env python
import pygame
from random import randrange
MAX_STARS = 100
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
stars = []
for i in range(MAX_STARS):
star = [randrange(0, 639), randrange(0, 479), randrange(1, 16)]
stars.append(star)
while True:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit(0)
screen.fill((0,0,0))
for star in stars:
star[0] -= star[2]
if star[0] < 0:
star[0] = 640
screen.set_at((star[0], star[1]), (255, 255, 255))
pygame.display.flip()
По логике этот язык очень похож на C, и их возможности сравнимы. В начале мы говорим Python, что хотим воспользоваться модулем Pygame и процедурой генерации случайных чисел randrange из модуля random, и затем объявляем переменную MAX_STARS, аналогичную эквиваленту в программе на C. Затем мы инициализируем Pygame и создаем окно, связывая его с переменной screen, после чего устанавливаем фоновый таймер с помощью функции Clock() библиотеки Pygame, чтобы немного замедлить происходящее.
Затем мы определяем массив star (или список – list – в терминах Python) выражением stars = []. Как видите, Python более гибок, чем C, и размер массива в начале задавать не нужно. Далее в цикле мы создаем 100 объектов star и заполняем каждый из них тремя значениями: координатой X, координатой Y и скоростью speed, как и в примере на C. После создания каждого объекта star мы добавляем его в список stars методом append.
Далее следует цикл while True, он основной. Сначала идет задержка clock.tick, затем мы велим Pygame обработать все пришедшие события от клавиатуры и мыши (по закрытию окна мы выходим из программы). Затем с помощью метода fill нашего объекта screen мы заливаем окно черным цветом и начинаем перебирать звезды в цикле: для каждой звезды вычитаем ее третий компонент (скорость, а точнее, перемещение за единицу времени) из первого (координата X), причем звезда перемещается влево (элементы массива или списка нумеруются с нуля). Как и в версии на C, мы проверяем выход звезды за левую границу окна.
После этого мы помещаем белый пиксель (255, 255, 255 в формате RGB) в точку с координатами X и Y, и на этом цикл star processing заканчивается. Наконец, мы вызываем процедуру display flip, которая выводит результаты всех предыдущих операций рисования на экран. В целом, программа на Python короче, проще и понятнее по сравнению с версией на C/SDL, и, как вы можете представить, писать игры в Pygame очень весело.
Необычный уровень: Perl и Ncurses
Наконец, рассмотрим лучший язык для обработки текста, который в равной мере любят и ненавидят – Perl. И, для вящей путаницы, мы будем рисовать звездное небо не в графическом, а в текстовом терминале. Как? Выводя точки в качестве звездочек! Есть очень полезная библиотека Ncurses, доступная для большинства языков программирования; она здорово упрощает работу с терминалом (перемещение, отключение курсора и т. д.). Вот код программы с LXFDVD из файла starfield.pl:
- !/usr/bin/perl
$numstars = 100;
use Time::HiRes qw(usleep);
use Curses;
$screen = new Curses;
noecho;
curs_set(0);
for ($i = 0; $i < $numstars ; $i++) {
$star_x[$i] = rand(80);
$star_y[$i] = rand(24);
$star_s[$i] = rand(4) + 1;
}
while (1) {
$screen->clear;
for ($i = 0; $i < $numstars ; $i++) {
$star_x[$i] -= $star_s[$i];
if ($star_x[$i] < 0) {
$star_x[$i] = 80;
}
$screen->addch($star_y[$i], $star_x[$i], “.”);
}
$screen->refresh;
usleep 50000;
}
Теперь вы должны хорошо понимать структуру этого кода. Единственное крупное отличие здесь – вместо массива звезд с координатами и скоростью для каждой (т. е. массива массивов) мы, чтобы упростить код, воспользовались тремя массивами. star_x содержит координаты X звезд, star_y – координаты Y, а star_s – скорости (как минимум единицу, чтобы все звезды двигались). Размер экрана 80 × 24 – стандартный размер терминала X Window, но для терминалов большего размера эти значения можно изменить.
Здесь addch – процедура печати символов Ncurses; и, что интересно, координата Y в ней указывается первой. usleep приостанавливает выполнение программы на заданное число микросекунд. Да, а команды noecho и curs_set в начале говорят, что нам не нужны ввод с клавиатуры и текстовый курсор.
Итак, мы узнали, как реализовать звездное небо на трех языках программирования с тремя инструментариями, и, надеюсь, это побудит вас попробовать новые языки и библиотеки. Как видите, основные алгоритмы в программе обычно одни и те же, но всегда можно узнать кое-что новое. Наслаждайтесь, и – удачи вам! |