Продолжение части 2. То что получилось в итоге можно посмотреть по ссылке:
dialas.ru/ink-dino/
Скрин игры на широкоформатном экране:
В этой статье будут рассмотрены финальные штрихи — оптимизация текста для вычитки, добавление картинок, музыки и общий дизайн странички.
#### Группировка текста
Для того, чтобы в будущем можно было дать на вычитку тексты я сгруппировал их в одном месте и объявил через константы:
//Узел для инициализации значений переменных
=== init_and_start_knot ===
...
CONST T_NOT_WORK = "Не сработало. "
CONST T_HUNTER_WAIT = "Охотник немного подождал. "
CONST T_THROW_FAILED = "Охотник не может подобрать удобную позицию чтобы метнуть копьё, зверюга очень изворотливая."
CONST T_THROW_BLIND = "Присмотревшись к кустам, охотник понимает, что вслепую атаковать бессмысленно."
CONST T_COME_CLOSER = "Пока динозавр скрывался охотник тихонько подкрался ближе к стволу дерева."
...
Правда, читаемость кода несколько снизилась:
//Состояния героя
=== function print_hero_state(state) ===
{
- state == far_tree:
~ return T_STATE_HERO_FAR
- state == near_tree:
~ return T_STATE_HERO_NEAR
- state == up_tree:
~ return T_STATE_HERO_ON_TREE
}
#### Анимированный логотип и финальная картинка
Небольшой ретро-налёт придаёт логотип из ASCII-графики с небольшой анимацией. Использовал Python-библиотеку asciimatics, нашел пару артов с деревом и охотником. Код под спойлером, при его запуске начинается анимация прямо в консоли. Далее просто записал с экрана. Кому интересно длинный код под спойлером, по сути просто переделка примера из библиотеки.
Спойлер
import random
from asciimatics.screen import Screen
from asciimatics.effects import Cycle, Print, Stars, Wipe, RandomNoise
from asciimatics.renderers import StaticRenderer, SpeechBubble, FigletText, Box
from asciimatics.scene import Scene
from asciimatics.screen import Screen
from asciimatics.sprites import Arrow, Plot, Sam, Sprite
from asciimatics.paths import Path
from asciimatics.exceptions import ResizeScreenError
import sys
last_blink = 2
def _blink2():
global last_blink
if last_blink == 2:
last_blink = 0
elif last_blink == 1:
last_blink = 2
elif last_blink == 0:
last_blink = 1
return last_blink
tree_anim = [
"""
. ;
. . ;% ;;
, , :;% %;
: ; :;%;' .,
,. %; %; ; %;' ,;
; ;%; %%; , %; ;%; ,%'
%; %;%; , ; %; ;%; ,%;'
;%; %; ;%; % ;%; ,%;'
`%;. ;%; %;' `;%%;.%;'
`:;%. ;%%. %@; %; ;@%;%'
`:%;. :;bd%; %;@%;'
`@%:. :;%. ;@@%;'
`@%. `;@%. ;@@%;
`@%%. `@%% ;@@%;
;@%. :@%% %@@%;
%@bd%%%bd%%:;
#@%%%%%:;;
%@@%%%::;
%@@@%(o); . '
%@@@o%;:(.,'
`.. %@@@o%::;
`)@@@o%::;
%@@(o)::;
.%@@@@%::;
;%@@@@%::;.
;%@@@@%%:;;;.
...;%@@@@@%%:;;;;,..
"""
]
fire_anim = [
"""
(
)
( (
)
( (
) /\ (
( // | (
_ -.;_/ \—-._
(_;-// | \ —'.\
( `.__ _ ___,')
`'(_ )_)(_)_)'
""",
"""
(
)
(
)
( (
)
( (
) /\ (
( // | (
_ -.;_/ \—-._
(_;-// | \ —'.\
( `.__ _ ___,')
`'(_ )_)(_)_)'
""",
"""
)
(
(
( (
)
( (
) /\ (
( // | (
_ -.;_/ \—-._
(_;-// | \ —'.\
( `.__ _ ___,')
`'(_ )_)(_)_)'
"""
]
hunter_wait_anim = [
"""
,&&&.
.,.&&
\=__/
,'-'.
_.__|/ /|
((_|___/ |
(( `'--|
\\ —._/.
<_,\_\`--'|
<_,-'__,'
"""
];
kust_anim = [
"""
|
\|/|/
\|\\|//|/
\|\|/|/
\\|//
\|/
\|/
|
_\|/__|_\|/____\|/_
"""
];
def _speak(screen, text, pos, start):
return Print(
screen,
SpeechBubble(text, "L", uni=screen.unicode_aware),
x=pos[0] + 4, y=pos[1] - 4,
colour=Screen.COLOUR_CYAN,
clear=True,
start_frame=start,
stop_frame=start+50)
class FireSprite(Sprite):
def __init__(self, screen, path, colour=Screen.COLOUR_WHITE, start_frame=0,
stop_frame=0):
super(FireSprite, self).__init__(
screen,
renderer_dict={
"default": StaticRenderer(images=fire_anim,
animation=_blink2),
"left": StaticRenderer(images=[fire_anim]),
"right": StaticRenderer(images=[fire_anim]),
"down": StaticRenderer(images=[fire_anim]),
"up": StaticRenderer(images=[fire_anim]),
},
path=path,
colour=colour,
start_frame=start_frame,
stop_frame=stop_frame)
def demo(screen):
scenes = []
centre = (screen.width // 2, screen.height // 2)
podium = (8, 5)
#Scene 0
effects = [
Cycle(
screen,
FigletText("DIALAS", font='big'),
screen.height // 2 - 8),
Cycle(
screen,
FigletText("PRESENTS", font='big'),
screen.height // 2 + 3),
#Stars(screen, (screen.width + screen.height) // 2)
]
scenes.append(Scene(effects))
#scene 0 to 1
effects = [
Cycle(
screen,
FigletText("DIALAS", font='big'),
screen.height // 2 - 8),
Cycle(
screen,
FigletText("PRESENTS", font='big'),
screen.height // 2 + 3),
RandomNoise(screen)
]
scenes.append(Scene(effects))
# Scene 1.
path = Path()
path.jump_to(centre[0], centre[1])
effects = [
Sprite(
screen,
renderer_dict={
"default": StaticRenderer(images=tree_anim)
},
path=path,
colour=Screen.COLOUR_GREEN)
]
scenes.append(Scene(effects))
# Scene 2.
path2 = Path()
path2.jump_to(centre[0]-45, centre[1]+7)
path3 = Path()
path3.jump_to(centre[0]-25, centre[1]+9)
#path.jump_to(-20, centre[1])
#path.move_straight_to(centre[0], centre[1], 10)
#path.wait(300)
effects = [
Sprite(
screen,
renderer_dict={
"default": StaticRenderer(images=tree_anim)
},
path=path,
colour=Screen.COLOUR_GREEN),
Sprite(
screen,
renderer_dict={
"default": StaticRenderer(images=hunter_wait_anim)
},
path=path3,
colour=Screen.COLOUR_WHITE),
FireSprite(screen, path2, colour=Screen.COLOUR_RED),
_speak(screen, "ПРОВЕРКА РУССКОГО", centre, 30)
]
scenes.append(Scene(effects))
screen.play(scenes, stop_on_resize=True)
Screen.wrapper(demo)
Финальную картинку нашел в интернете и немного обработал под рисунок карандашем, всё просто.
#### Дизайн темы
Тему решил взять подходящую под доисторические приключения. Я сам мало что понимаю в дизайне и не люблю копаться в коде верстки, поэтому нашел уже готовую из бесплатных: chocotemplates.com/portfolio/dinosaurs. Сгенерировал веб-версию через графический редактор ink и сделал дополнения:
* В style.css поставил фон, настроил похожие цвета шрифтов и кнопки
* В index.html убрал ссылки что написан на ink, для большего погружения в игру.
* В main.js сделал удаление предыдущего текста, так как надо описание только последней сцены (потом узнал про тэг #CLEAR, но решил оставить по-старому)
#### Музыка
Взял саундтрек с сайта www.jamendo.com и добавил кнопку для выключения музыки:
<audio id="player" src="Marian_W_-_Darkbreeze_-_Hunter_s_Legend.mp3" autoplay loop></audio>
<div>
<button onclick="document.getElementById('player').pause()">Выкл. звук</button>
</div>
Так как не сильно разбираюсь во front-end разработке, не стал делать включения звука под разные браузеры и вывод текста что аудио не будет работать.
#### Выводы
Хочу сказать, что получился занятный эксперимент с ink по разработке небольшой игры. В статьях я старался показать как с помощью автоматного подхода можно спроектировать и реализовать неплохую головоломку или целую игровую механику. Пишите комментарии, что думаете об игре. Всем спасибо!
Ссылка на онлайн-версию: dialas.ru/ink-dino
Скачать архив с веб-версией: cloud.mail.ru/public/276U/1ph5r3Y2L
Исходник игры на ink: pastebin.com/N3PKr6fp
Ссылка:
https://ifhub.club/2018/09/12/konechnye-avtomaty-v-menyushnom-dvizhke-oformlyaem-doistoricheskoe-priklyuchenie-na-ink-chast-3.html