Вдохновившись отзывами на первую статью я продолжил разработку piu-piu. В игре появилось интро\меню, реализовано посимвольное появление объектов, изменилось цветовое решение. Палитра теперь определяется по времени года, правда, из-за недостатка цветов пришлось ограничиться 3-мя вариантами: зима — начало весны, весна — лето и осень. Изменения можно оценить, скачав игру тут. Далее немного букв как это все получилось.
Я начал с реализации посимвольного вывода объектов. Что может быть проще? Просто сделай срез элемента спрайта, но…
Управляющие коды (цвет) срежутся, и вместо цветного символа получится каша. Необходимо пересобрать элемент спрайта посимвольно, вставляя нужные управляющие коды перед каждым символом. Поприсядав изрядное количество времени со срезами я, наконец, разработал похожий алгоритм. Тестовый сценарий:
#!/bin/bash
# подключаю свою табличку с красками
. ~/SCR/color
# тестовый спрайт
sprite=(
'/?????\'
'| 0 |'
'\_____/'
)
# расцветка тестового спрайта
sprite_color=(
"$RED"
"$GRN"
"$BLU"
)
# уже знакомые функции выхода и определения размеров
function bye () {
stty echo
printf "${CON}${DEF}"
clear
ls --color=auto
exit
}
function get_dimensions {
size=($(stty size))
endx=${size[1]}
endy=${size[0]}
}
# инициализация
get_dimensions
X=$endx
Y=$[$endy/2]
sn=${#sprite[@]} # количество элементов спрайта
sl=${#sprite[0]} # ширина спрайта, считаетсяя по наибольшему элементу
trap bye INT; stty -echo; printf "${COF}"; clear
# цикл
while true; do sleep 0.1; get_dimensions
for (( p=0; p<${sn}; p++ )); do # цикл по элементам спрайта
end=$[1-(${X}-${endx})]
if [ $X -gt 0 ]; then
bgn=0
XY ${X} $(($Y + $p)) # цвет | срез спрайта
"${sprite_color[$p]}${sprite[$p]:${bgn}:${end}} ${DEF}"
else
bgn=$[1-$X]
XY 1 $(($Y + $p)) "${sprite_color[$p]}${sprite[$p]:${bgn}:${end}} ${DEF}"
fi
done
((X--)); [ $X -lt -${sl} ] && X=$endx
done
Получилось так:
Но этот алгоритм позволяет «красить» только строки целиком, это не было пределом мечтаний и не соответствовало существующей раскраске спрайтов. Пришлось поприседать еще. Таблица цветов расширилась, для каждого символа определен свой цвет. Каждый элемент массива цветов преобразуется в отдельный массив, индексы которого совпадают с индексами элемента спрайта. Затем элемент цвета и элемент спрайта рисуются в нужном месте и в нужное время.
#!/bin/bash
. ~/SCR/color
# новый тестовый спрайт
sprite=(
' /?????\ '
'-----|12345|-------- '
' --|12345|--- '
' ----|12345|---- '
' \_____/ '
)
# расцветка тестового спрайта, свой цвет для каждого символа
C01=$UND$BLU; C02=$UND$BLD$BLU C03=$DEF$MGN
sprite_color=(
"$DEF $DEF $DEF $DEF $DEF $DEF $DEF"
"$DEF $RED $GRN $C01 $C03 $YLW $DEF $RED $DEF $DEF $DEF $BLU $DEF"
"$DEF $RED $GRN $C02 $C03 $YLW $DEF $DEF $GRN $DEF $DEF $DEF $DEF"
"$DEF $RED $GRN $C01 $C03 $YLW $DEF $DEF $DEF $BLU $DEF $DEF $DEF"
"$DEF $DEF $DEF $DEF $DEF $DEF $DEF"
)
function bye () {
stty echo
printf "${CON}${DEF}"
clear
ls --color=auto
exit
}
function get_dimensions {
size=($(stty size))
endx=${size[1]}
endy=${size[0]}
}
get_dimensions
X=$endx
Y=$[$endy/2]
L=1
sn=${#sprite[@]}
sl=${#sprite[1]}
trap bye INT; stty -echo; printf "${COF}"; clear
while true; do sleep 0.1; get_dimensions
((L++)); ((X--)); [ $X -lt -${sl} ] && { X=$endx; L=1; }
for (( p=0; p<${sn}; p++ )); do
color=(${sprite_color[p]}); YY=$[$Y+${p}]
# вложенные циклы по символам элементов спрайта
if [ $X -gt 1 ]; then
for (( k=0; k<${L}; k++ )); do
XY $[${k}+${X}] ${YY} "${color[$k]}${sprite[$p]:$k:1}"
done
else
for (( k=1; k<${sl}; k++ )); do
XY ${k} ${YY} "${color[$[$k-$X]]}${sprite[$p]:$[$k-$X]}"
done
fi
done
done
Теперь все символы могут быть разноцветными:
Отлично! Я решил проверить как это будет рисоваться поверх другого спрайта. Добавил статический спрайт в основной цикл:
for (( p=0; p<${sn}; p++ )); do
XY 3 $[${endy}/2+${p}] "${sprite[$p]}"
done
И проверил:
Нормально, но если сзади «маска» получается сама собой, то передняя часть рисуется квадратом, затирая все, опять присядания.
#!/bin/bash
. ~/SCR/color
# у спрайта появились индексы смещения
sprite=(
5' /?????\ '
0' -----|12345|-------- '
3' --|12345|--- '
1' ----|12345|---- '
5' \_____/ '
)
# немного изменил таблицу расцветки
sprite_color=(
"$DEF"
"$DEF $DEF $DEF $DEF $DEF $DEF $DEF $MGN $YLW $grn $RED $MGN $DEF"
"$DEF $DEF $DEF $DEF $DEF $DEF $DEF $BLU $GRN $cyn $ylw $blu $DEF"
"$DEF $DEF $DEF $DEF $DEF $DEF $DEF $YLW $RED $BLU $grn $YLW $DEF"
"$DEF"
)
function bye {
stty echo
printf "${CON}${DEF}"
clear
ls --color=auto
exit
}
function get_dimensions {
size=($(stty size))
endx=${size[1]}
endy=${size[0]}
}
get_dimensions
X=$endx
Y=$[$endy/2]
L=1
sn=${#sprite[@]}
sl=${#sprite[1]}
trap bye INT; stty -echo; printf "${COF}"; clear
while true; do sleep 0.1; get_dimensions
# фоновый спрайт
for (( p=0; p<${sn}; p++ )); do
XY 3 $[${endy}/2+${p}] "${sprite[$p]}"
done
# увеличиваю циферки
((L++)); ((X--))
[ $X -lt -${sl} ] && { X=$endx; L=1; }
for (( p=0; p<${sn}; p++ )); do
# определяю цвет
color=(${sprite_color[p]})
# координату Y
YY=$[$Y+${p}]
# смещение начала
stp=${sprite[$p]:0:1}
# срез спрайта
spr=${sprite[$p]:1}
if [ $X -gt 1 ]; then
for (( k=0; k<${L}; k++ )); do
if [ $k -ge $stp ]; then
XY $[${k}+${X}] ${YY} "${DEF}${color[$k]}${spr:$k:1}"
fi
done
else
for (( k=1; k<${sl}; k++ )); do
XY ${k} ${YY} "${color[$[$k-$X]]}${spr:$[$k-$X]}"
done
fi
done
done
Красота!
Баш позволяет делать срезы как переменных, так и массивов. Пример, срез переменной:
a=1234567890
# отрезали 3 символа вначале
echo ${a:3}
4567890
# вырезали 3 символа из тела
echo ${a:3:3}
456
# можно указывать отрицательное значение второго элемента среза, это подрежет конец
echo ${a:(-4)}
123456
echo ${a:(-5):(-2)}
678
А теперь срез массива:
a=( 1 2 3 4 5 6 7 8 9 0 )
# отрезали 3 элемента вначале
echo ${a[*]:3}
4 5 6 7 8 9 0
# вырезали 3 символа из тела
echo ${a[*]:3:3}
# срез с конца
echo ${a[@]:(-5)}
6 7 8 9 0
# а так почему-то нельзя
echo ${a[@]:(-5):(-1)}
bash: (-1): выражение подстроки < 0
Помню, во времена Спектрума ты не мог называться крутым кодером, если не сделал крутой скроллер. И все крутые парни делали их(мы тоже). Во всех демках были скроллеры с бесконечными гритингсами, матами, шутками-прибаутками. Хорошее было время. Попробуем запилить скроллер на BASH'е, используя срезы конечно:
#!/bin/bash
# подключаю палитру и функцию рисования XY
. ~/SCR/color
# текст, можно задать параметром
text=${1:-'Hello Wo00000o0000000o000orld! '}; N=${#text}
# опять эти функции
function bye {
stty echo
printf "${CON}${DEF}"
clear
ls --color=auto
exit
}
function get_dimensions {
size=($(stty size))
endx=${size[1]}
endy=${size[0]}
}
# инициализация
get_dimensions
trap bye INT
stty -echo
printf "${COF}"
X=$[$endx+1]
Y=$[$endy/2]
L=0; clear
# цикл
while true; do sleep 0.05; get_dimensions
[ $X -gt 1 ] && XY $X $Y "${text:0:$L}" || XY 1 $Y "${text:$[1-$X]:$L}"
[ $X -lt -$N ] && { X=$endx; L=0; } || ((X--))
[ $L -lt $endx ] && ((L++))
done
Привет мир!
А если прикрутить сюда figlet, получится вообще хорошо:
#!/bin/bash
. ~/SCR/color
text=${1:-'Hello Wo00000o0000000o000orld! '}; N=${#text}
function bye {
stty echo
printf "${CON}${DEF}"
clear
ls --color=auto
exit
}
function get_dimensions {
size=($(stty size))
endx=${size[1]}
endy=${size[0]}
}
get_dimensions
trap bye INT
stty -echo
printf "${COF}"
IFSOLD=$IFS
IFS=$'\n'
# добавился фиглет
figlet_text=( $(figlet -w$[$N*10] "${text}") )
IFS=$IFSOLD
NF=${#figlet_text[1]}
NFL=${#figlet_text[*]}
X=$[$endx+1]
Y=$[$endy/2-$NFL/2]
L=0; clear
while true; do sleep 0.05; get_dimensions
if [ $X -gt 1 ]; then
# цикл по элементам фиглетового текста
for ((i=0; i<$NFL; i++)); do
XY $X $[$Y+$i] "${figlet_text[$i]:0:$L}"
done
else
for ((i=0; i<$NFL; i++)); do
XY 1 $[$Y+$i] "${figlet_text[$i]:$[1-$X]:$L}"
done
fi
[ $X -lt -$NF ] && { X=$endx; L=0; } || ((X--))
[ $L -lt $endx ] && ((L++))
done
Тут есть одна тонкость. Я переопределяю символ «разделитель». По умолчанию разделителями являются: пробел, таб, переход строки. Все это хранится в системной переменной IFS. Я запоминаю старое значение:
IFSOLD=$IFS
Затем устанавливю разделителем только переход строки \n:
IFS=$'\n'
Офиглеваю текст в массив figlet_text командой:
figlet_text=( $(figlet -w$[$N*10] "${text}") )
Ключ -w задает фиглету ширину строки, т.к. по умолчанию он вписывает текст в экран. А это добавляет ненужные переходы строки, и чуда не происходит. Ну и возвращаю старое значение разделителя:
IFS=$IFSOLD
Фиглетовый скроллер!
Итак, спрайты режутся, скорей впихивай этот алгоритм в игру! Я попробовал и о… очень сильно расстроился. Скорость, мягко говоря, оставляла желать лучшего:
Множественные вложенные циклы отрицательно сказываются на быстродействии (внезапно). А ведь я пробовал без деревьев, облаков и прочей мишуры, мда.
Но после такого количества присяданий не хотелось выбрасывать идею на помойку. Моск начал работать. Решение было найдено. Использовать оба метода! Юху! Появление\исчезновение посимвольно а полет по экрану «быстрым» методом. Цикл объектов теперь выглядит так:
NO=${#OBJ[@]}
for (( i=0; i<$NO; i++ )); do
OI=(${OBJ[$i]})
OX=${OI[0]}
OY=${OI[1]}
cuter=${OI[2]}
type=${OI[3]}
case $type in
# объекты с меняющимися спрайтами(быстрый\медленный)
# дерево 1 медленный спрайт
"tree1" ) sprite=("${tree1[@]}")
# палитра медленного спрайта
sprite_color=("${tree1_color[@]}")
# быстрый спрайт
sprite_fast=("${tree12[@]}")
# функция - двигатель
# +---------+------+------+------+
# | функция |таймер|высота|ширина|
# +---------+------+------+------+
mover $Q 4 4;;
# дерево 2
"tree2" ) sprite=("${tree2[@]}")
sprite_color=("${tree2_color[@]}")
sprite_fast=("${tree22[@]}")
mover $W 6 6;;
# дерево 3
"tree3" ) sprite=("${tree3[@]}")
sprite_color=("${tree3_color[@]}")
sprite_fast=("${tree32[@]}")
mover $E 9 10;;
# облако 1
"cloud1") sprite=("${cloud1[@]}")
sprite_color=("${cloud1_color[@]}")
sprite_fast=("${cloud12[@]}")
mover $Q 3 7;;
# облако 2
"cloud2") sprite=("${cloud2[@]}")
sprite_color=("${cloud2_color[@]}")
sprite_fast=("${cloud22[@]}")
mover $W 3 9;;
# облако 3
"cloud3") sprite=("${cloud3[@]}")
sprite_color=("${cloud3_color[@]}")
sprite_fast=("${cloud32[@]}")
mover $E 3 12;;
# враги
"alien" ) sprite=("${alien[@]}")
sprite_color=("${alien_color[@]}")
sprite_fast=("${alien2[@]}")
mover 0 3 5;;
# объекты с только быстрыми спрайтами
# выстрел босса
# быстрый спрайт
"bfire" ) sprite=("${bfire[@]}")
# функция - двигатель
# +---------+------+------+------+
# | функция |таймер|высота|ширина|
# +---------+------+------+------+
mover 0 6 4;;
# бонус - патроны
"ammo" ) sprite=("${ammob[@]}")
mover 0 3 4;;
# бонус - жизнь
"life" ) sprite=("${lifep[@]}")
mover 0 3 4;;
# бонус - усилитель ствола
"gunup" ) sprite=("${gunup[@]}")
mover 0 3 4;;
# взрывы, не латают, рисуем 1 раз
"boom" ) er=${boomC}
for part in "${boom[@]:$B:$boomC}"; do
stp=${part:0:1}
spr=${part:1}
XY $[${OX} + $stp] ${OY} "${spr}"; ((OY++))
done
[ ${E} = 0 ] && { ((B+=${boomC}))
[ $B -gt ${boomN} ] && { B=0; erase_obj ${i}; }; };;
esac; done
А вот функция mover:
function mover () {
er=$2 # кол-во линий спрайта
width=$3 # ширина спрайта
# плюсуем циферки
[ ${1} = 0 ] && {
((OX--))
((cuter++))
OBJ[$i]="$OX $OY $cuter $type"
}
# не улетел ли объект
case ${type} in
'alien'|'tree'[1-3]|'cloud'[1-3])
[ $OX -lt -$width ] && {
remove_obj ${i}
case ${type} in
"alien") ((enumber--));;
esac; return
};;
*)
[ $OX -lt 1 ] && {
erase_obj ${i}
case ${type} in
"alien") ((enumber--));;
esac; return; };;
esac
# рисовалка
for (( p=0; p<${er}; p++ )); do
case ${type} in
# быстрые\медленные спрайты
'alien'|'tree'[1-3]|'cloud'[1-3])
color=(${sprite_color[$p]})
YY=$[$OY+${p}]
stp=${sprite[$p]:0:1}
spr=${sprite[$p]:1}
# прилетает\летит
if [ $OX -gt 1 ]; then
if [ $cuter -lt $width ]; then
# прилетает, посимвольный вывод
for (( k=0; k<${cuter}; k++ )); do
if [ $k -ge $stp ]; then
XY $[$k+$OX] $YY "${color[$k]}${spr:$k:1}"
fi
done
else
# летит, переключение на быстрый спрайт
stp=${sprite_fast[$p]:0:1}
spr=${sprite_fast[$p]:1}
XY $[${OX} + $stp] $[$OY + $p] "${spr}"
fi
# улетает
else
# опять посимвольно
for (( k=1; k<${width}; k++ )); do
x=$[$k-$OX]
XY $k $YY "${color[$x]}${spr:$x}"
done
fi;;
# только быстрые спрайты
*) XY ${OX} $[$OY + $p] "${sprite[$p]}";;
esac
# проверка коллизий
case ${type} in
"gunup" )
case "$[$OY + $p] $OX" in
"$HY $HX")
[ ${G} -lt 4 ] && ((G++))
erase_obj ${i}
break;;
esac;;
"life" )
case "$[$OY + $p] $OX" in
"$HY $HX")
((life++))
erase_obj ${i}
break;;
esac;;
"ammo" )
case "$[$OY + $p] $OX" in
"$HY $HX")
((ammo+=100))
erase_obj ${i}
break;;
esac;;
"bfire" )
case "$OY $OX" in
"$HY $HX")
((life--))
erase_obj ${i}
break;;
esac;;
"alien" )
# столкновение с пулей
for (( t=0; t<${NP}; t++ )); do
case "$[$OY + 1] $[$OX + $p]" in
"${PIU[$t]}") # есть бонус?
if [ $[RANDOM % $rnd] -eq 0 ]; then
OBJ+=("$OX $OY 0 ${bonuses[$[RANDOM % ${#bonuses[@]}]]}")
((frags++))
((enumber--))
remove_obj ${i}
remove_piu ${t}
OBJ+=("${OX} ${OY} 0 boom")
break
fi;;
esac
done
# столкновение с героем
case "$[$OY + 1] $[$OX + $p]" in
"$HY $HX")
((life--))
((frags++))
((enumber--))
remove_obj ${i}
OBJ+=("${OX} ${OY} 0 boom")
break;;
esac;;
esac
done
}
Пришлось нарисовать по 2 комплекта спрайтов для обоих методов вывода. И ограничить количество одновременно вылетающих объектов. Для чужих я добавил в условие появления тайминг. А деревьям\облакам уменьшил вероятность появления. Вот какие спрайты получились, на примере дерева:
# "медленный" спрайт
tree3=(
3' _._ '
2' / \ '
1' _\ | / '
0'/ \¦/__ '
0'\_\/¦/ \ '
3' \¦|/_/ '
4' ¦/ '
4' ¦ '
4' ¦ ')
# основной цвет меняется в зависимости от времени года
case $month in
0[1-4]|12) CLR=${cyn} ;; # зима
0[5-8] ) CLR=${BLD}${GRN};; # лето
09|1[0-1]) CLR=${DIM}${red};; # осень
esac
CM1=${SKY}${BLK}
tree3_color=(
"${SKY} ${SKY} ${SKY} ${CLR} ${CLR} ${CLR} ${SKY}"
"${SKY} ${SKY} ${CLR} ${SKY} ${SKY} ${SKY} ${CLR} ${SKY}"
"${SKY} ${CLR} ${CLR} ${SKY} ${CM1} ${SKY} ${CLR} ${SKY}"
"${CLR} ${SKY} ${SKY} ${CLR} ${CM1} ${CLR} ${CLR} ${CLR} ${SKY}"
"${CLR} ${CLR} ${CM1} ${CLR} ${CM1} ${CLR} ${SKY} ${SKY} ${CLR} ${SKY}"
"${SKY} ${SKY} ${SKY} ${CM1} ${CM1} ${CLR} ${CM1} ${CLR} ${CLR} ${SKY}"
"${SKY} ${SKY} ${SKY} ${SKY} ${CM1} ${CM1} ${SKY}"
"${SKY} ${SKY} ${SKY} ${SKY} ${CM1} ${SKY}"
"${SKY} ${SKY} ${SKY} ${SKY} ${CM1} ${SKY}")
# "быстрый" спрайт
tree32=(
3${CLR}'_._ '${SKY}
2${CLR}'/ \ '${SKY}
1${CLR}'_\ '${CM1}'|'${CLR}' / '${SKY}
0${CLR}'/ \\'${CM1}'¦'${CLR}'/__ '${SKY}
0${CLR}'\_'${CM1}'\\'${CLR}'/'${CM1}'¦'${CLR}'/ \ '${SKY}
3${BLK}'\¦'${CLR}'|'${CM1}'/'${CLR}'_/ '${SKY}
4${BLK}'¦/ '${SKY}
4${BLK}'¦ '${SKY}
4${BLK}'¦ '${SKY})
И тут мы плавно переходим к следующей фиче. Основной цвет деревьев, облаков и фона меняется в зависимости от времени года. Время года определяется по месяцу. Месяц определяется date'ом:
month=$(date +'%m')
В разное время года игра будет выглядеть по разному. Сейчас так, осень:
А скоро будет так, зима — начало весны:
А так будет совсем не скоро. Весна — лето:
Но читеры могут сделать так:
month=07
Да, пулялка может увеличиваться до х5, и босс немного подрос.
А вот такое интро\меню появилось в игре:
Но тут тоже не все было гладко в плане быстродействия. Большие спрайты даже поодиночке жутко тормозили в посимвольном режиме вывода. Пришлось делать присядания опять, да.
Я решил нарезать спрайты на куски по 3 символа и выводить их «быстрым» методом:
D=$DEF; C1=$BLU; C2=$RED; C3=$YLW; C4=$red
C5=$BLD$YLW; C6=$BLD$GRN; C7=$blu; C8=$BLD$RED
# - --- -- - -- -- -- -
# - - ------- -------- ---- ---
# - ------ ---- ------ ---- ------- -
#- - ------ ---- ------ ------ -----
# --------- ----- ----- ------- - -
# - -------- - ----- ---------- -
# ---------- - ------- ---------- -
#- ------ - --------- - -------
# - -- - - - ------- - ------- -
piu=(
"$C1 " "- " " --" "- -" "- " " " "- " "-- " " " " -" "- " "-- " "-$D " " "
"$C1 - " " -" " $C2-" "$C3---" "---" " $C1-$C2-" "$C3---" "---" " $C2-$C3-" "-$C1- " " $C2-$C3-" "-$D " " " " "
"$C1 " "- -" "-$C2-$C4-" "$C3-- " "$C2-$C4-$C3-" "- $C4-" "--$C3-" "-$C4- " "$C1-$C4-$C3-" "- $C1-" "$C2-$C4-$C3-" "-$C1--" " -$D " " "
"$C1- -" " --" "$C2-$C4-$C3-" "- $C2-" "$C4-$C3--" " $C2--" "-$C4-$C3-" "- $C2-" "-$C4-$C3-" "-$C1- " "$C2-$C4-$C3-" "-$C1-$D " " " " "
"$C1 " " " "$C2-$C4-$C5-" "---" "--$C1-" " -$C2-" "$C4-$C3--" " $C1-$C2-" "$C4-$C3--" " $C1-$C2-" "$C4-$C3--" "$C1-- " "- -$D" " "
"$C1 -" " $C2-" "$C4-$C3--" "$C4---" "- " "$C1- $C2-" "$C4-$C3--" "$C1- $C2-" "$C4-$C3--" "$C1--$C2-" "$C4-$C3--" " $C1-$D " " " " "
"$C2 " "-$C4-$C3-" "---" "$C2---" "$C1- -" " $C4-$C3-" "---" "-- " "$C1-$C4-$C3-" "---" "--$C1-" "- -$D" " " " "
"$C1- -" "$C2-$C4--" "-- " "$C1- -" "-$C2-$C4-" "---" "-- " "$C1- $C2-" "$C4---" "--$C1-$D" " " " " " " " "
"$C1 - " "-- " "- -" " - " "---" "---" "- -" " --" "---" "-- " " - " " " " " "$D ")
piuN=${#piu[*]}; piuC=14
# -- ------
# --- ----- ------
# - ---------- -------- -- -
#- ----- -------------
# - -- -----------
# --- --- -----
arr=(
" " " " " " " " " $C7--" " $C3--" "$C6---" "$C7-$D " "" ""
" " " " " $C7-" "-- " "$C2-$C8--" "-- " "$C7-$C3-$C6-" "--$C7-$D" "" ""
" " " $C7- " "-$C8--" "--$D$C2-" "$C3---" "- $C7-" "-$C7-$C3-" "-$C6--" "- $C7-" "- -$D"
" " "$C7- $C2-" "$C3---" "- $C2-" "$C8---" "-$D$C7-$C3-" "$C3-$C6--" "-$C7--$D" " " ""
" " " $C7- " "-- " " $C2-$C3-" "---" "$C7-$C3-$C6-" "--$C7-$D" " " "" ""
" " " " " $C7--" "- -" "-- " "---" "--$D " " " "" "")
arrN=${#arr[*]}; arrC=10
Рисуется вся эта красота своими функциями left, right и intro:
function left () { N=$1; C=$2
# move
[ $OX -ge $end ] && {
((OX-=3)); [ $cuter -ne $C ] && ((cuter++))
for ((j=0; j<$N; j+=$C)); do
line=("${sprite[@]:$j:$cuter}")
YY=$[$OY+$j/$C]; spr=
for part in "${line[@]}"; dospr+="${part}"; done
XY $OX $YY "${spr}"
done; OBJ[$i]="$OX $OY $cuter $end $type $pause"
} || { remove_obj $i; ((Q++)); OBJ+=("${scenario[$Q]}"); }
}
function right () { N=$1; C=$2
# move
[ $OX -le $end ] && {
[ $cuter -ne $C ] && ((cuter++)) || ((OX+=3))
for ((j=0; j<$N; j+=$C)); do
line=("${sprite[@]:$j:$C}")
YY=$[$OY+$j/$C]; spr=
for ((k=$[$C-$cuter]; k<$C; k++)); do spr+="${line[k]}"; done
XY $OX $YY "${spr}"
done; OBJ[$i]="$OX $OY $cuter $end $type $pause"
} || { remove_obj $i; ((Q++)); OBJ+=("${scenario[$Q]}"); }
}
function intro { get_dimensions; Q=0
scenario=(
# сценарий появления объектов
#----------+-------+-----+------------+----+
# старт X |старт Y|резак| конец X |тип |
#----------+-------+-----+------------+----+
"$[$endx+1] 3 0 $[endx/2-34] piu"
"$[$endx+1] 3 0 $[endx/2+2] piu"
"-2 12 0 $[endx/2-16] arr"
"0 0 0 0 end")
OBJ=("${scenario[$Q]}")
while true; do sleep 0.005; NO=${#OBJ[@]}
for (( i=0; i<$NO; i++ )); do
OI=(${OBJ[$i]}); OX=${OI[0]}; OY=${OI[1]}
cuter=${OI[2]}; end=${OI[3]}; type=${OI[4]}
case $type in
#-----+-------------------+------+------+------+
#тип | спрайт |функц.|высота|ширина|
#-----+-------------------+------+------+------+
"arr") sprite=("${arr[@]}"); right $arrN $arrC;;
"piu") sprite=("${piu[@]}"); left $piuN $piuC;;
"end") return ;;
esac
done
done
}
Последовательность появления объектов задается в массиве scenario. Пункт меню conf дает возможность кастомизации (небольшой) самолетика. Можно изменить цвет самолетика и символ на хвосте (и его цвет).
И еще кучка мелких доработок\исправлений по ходу написания двух статей (к слову о пользе статей). Дальше хочется поработать над оптимизацией, приподнять фпс и попробовать реализовать кооператив по сети, продолжение следует. На этом пока все (где-то я это слышал).
С праздником! Пиу, пиу, пиу! :)
Комментарии (39)
ZubroZuu
13.09.2017 22:27+2Спасибо за статью, не останавливайтесь, будем ждать продолжения развития проекта.
Self_Perfection
13.09.2017 23:07Так-с, со второй статьи я не удержался и попробовал посмотреть код. shellcheck вам бы на него натравить было бы неплохо
*ушёл пилить пулреквесты*
saboteur_kiev
13.09.2017 23:22+3А если повернуть игру на 90 градусов, и лететь сверху вниз, это разве не увеличит быстродействие естественным способом?
vaniacer Автор
13.09.2017 23:30+2Да, но это будет вертикальный скроллер а мне больше нравятся горизонтальные.
Забегая вперед, я уже поработал немного над быстродействием, новый алгоритм почти в 2 раза быстрей. Вероятно, и это не предел. Но об этом в следующей серии)
synedra
14.09.2017 06:47+1Сверху вниз для шмапа очень нетрадиционно и может расстраивать многих игроков. Обычно или слева направо, или снизу вверх.
ZyXI
14.09.2017 01:17+1А почему «цветов не хватает»? Во многих терминалах есть поддержка 24?битного цвета, что больше либо равно числу цветов, поддерживаемых большинством мониторов.
PS: не думали попробовать написать что?то сложное под POSIX shell? Я как?то аналог getopt --longoptions писал, использовал много
eval
для «массивов», но вопрос о скорости, к счастью, не стоял.vaniacer Автор
14.09.2017 08:42Да, я уже думаю о изменении палитры. Eval пробовал, очень тормозно получается.
synedra
14.09.2017 06:49+4Потрясающий проект. Вы не думали отправить его в репозитории дебиана/убунты? Названия `piu` и `piupiu` свободны, а желающего скачать подобное народу наверняка найдётся немало.
barsuksergey
14.09.2017 12:55Класс. Поддерживаю synedra, игра уже заслуживает официального репозитория.
Очень красивые очереди летят теперь, если зажать огонь. Всё замедляется, как в Матрице.
После Well Done, может, хочется снова в игру, а она или завершается, или вылетает, не понял.
AVX
14.09.2017 15:34Жесть конечно. Напомнило про вот это: pinguem.ru/event/bash-golf
Вот где народ поизвращался знатно… (а, да, я тоже поучаствовал).
Правда, там не было ограничений что внутри скрипта будет, и все перешли на python, perl.
Incognito4pda
Красота!
vaniacer Автор
Спасибо)