Серия «Пишу на kotlin»

3

Капля крови для Грегора

Ура, я написал визуальную новеллу про вампиров!

В игре нет рекламы, нет платного контента, я не ищу спонсоров, не пытаюсь её продать. А хочу донести её до аудитории. Возможно найдутся те, кто пройдет её и останется в хорошем впечатлении. А еще ищу человека на роль сценариста (за бесплатно, само собой 😁 - игра распространяется свободно, платить мне не из чего), чтобы продолжить и улучшить сюжет, так как пока по части сюжета получаю некоторую критику. По части грамматики тоже:)

Писал игру сам в Android Studio, не без помощи нейросети. Выношу на суд Пикабу. Если кому интересно, могу написать пост о том, как это вообще делалось и из чего состоит. Маленький спойлер - тут не использовался Unity, не использовались библиотеки или фреймворки для подобных игр и игр вообще, написал на чистом android стеке, который использую в работе над продуктовыми приложениями. Почему? Умею и могу себе позволить.

В игре есть два сюжета: подлиннее и покороче. Тот что подлиннее за девушку Лилиан, короткий сюжет за механического Бернарда. В перспективе, есть желание сделать третий за вампира Грегора во времена его юности и устроить сюжеты перекликающимися. Идея в том, чтобы действия в одном сюжете могли менять события и возможности для других. С технической точки зрения реализовать это не сложно, сложно писать сюжет и понять, что должно произойти и где, и как это будет влиять на другие сюжеты, чтобы не ломать логику и не вызывать у игрока чувство несогласия. Для сцен, где не предусмотрена подложка, я использовал процедурную генерацию символов, которую писал вообще для другого приложения, но она очень гармонично сюда легла.

Каждый символ в колонке генерируется случайно, также как и точка появления колонки. Длинна колонки и её продолжительность, а также количество колонок заданы константами.

Каждый символ в колонке генерируется случайно, также как и точка появления колонки. Длинна колонки и её продолжительность, а также количество колонок заданы константами.

Сейчас игру установить можно только из Google Play и только на устройство под управлением android. https://play.google.com/store/apps/details?id=com.pavlovalexey.adropofbloodforgregor

Показать полностью 5
16

Процедурная анимация фона в стиле Матрицы

Код генерирует набор символов катакана. Символы хирагана, катакана и кандзи использовались в анимации экранов операторов подключения к матрице. Каждая дорожка как бы показывала происходящие события, а оператор по символам мог считывать происходящее в блоке кода матрицы.

Сделал для своего приложения такую красоту, внизу код на Kotlin (Android+Compose), пользуйтесь кто желает (нейросетью можете перевести на свой язык под свою платформу). Сразу после списка символов идут переменные управления потоками, вынес их вместе, чтобы регулировать скорость, частоту и прочее.



import androidx.compose.foundation.background

import androidx.compose.foundation.layout.*

import androidx.compose.material3.Text

import androidx.compose.runtime.*

import androidx.compose.ui.Modifier

import androidx.compose.ui.graphics.Color

import androidx.compose.ui.platform.LocalConfiguration

import androidx.compose.ui.platform.LocalDensity

import androidx.compose.ui.unit.dp

import androidx.compose.ui.unit.sp

import kotlinx.coroutines.delay

import kotlinx.coroutines.isActive

import kotlin.random.Random

object MatrixAnimationSettings {

val symbols = listOf(

'ア', 'ィ', 'イ', 'ゥ', 'ウ', 'ェ', 'エ', 'ォ', 'オ', 'カ', 'ガ', 'キ', 'ギ', 'ク', 'グ', 'ケ', 'ゲ', 'コ', 'ゴ',

'サ', 'ザ', 'シ', 'ジ', 'ス', 'ズ', 'セ', 'ゼ', 'ソ', 'ゾ', 'タ', 'ダ', 'チ', 'ヂ', 'ッ', 'ツ', 'ヅ', 'テ', 'デ',

'ト', 'ド', 'ナ', 'ニ', 'ヌ', 'ネ', 'ノ', 'ハ', 'バ', 'パ', 'ヒ', 'ビ', 'ピ', 'フ', 'ブ', 'プ', 'ヘ', 'ベ', 'ペ',

'ホ', 'ボ', 'ポ', 'マ', 'ミ', 'ム', 'メ', 'モ', 'ャ', 'ヤ', 'ュ', 'ユ', 'ョ', 'ヨ', 'ラ', 'リ', 'ル', 'レ', 'ロ',

'ヮ', 'ワ', 'ヰ', 'ヱ', 'ヲ', 'ン', 'ヴ', 'ヵ', 'ヶ', 'ヷ', 'ヸ', 'ヹ', 'ヺ', '・', 'ー', 'ヽ', 'ヾ'

)

const val rows = 15 // количество дорожек с символами

const val maxVisibleSymbols = 70 // Максимальное количество видимых символов

const val symbolDelay = 200L // Задержка между появлениями символов

const val fadeStep = 0.1f // Шаг уменьшения альфы

const val alphaStart = 1f // Начальное значение альфы

const val maxYOffset = 100 // Максимальное вертикальное смещение

const val maxXOffset = 10 // Максимальное горизонтальное смещение

const val maxDelay = 10000L // Макс задержка

const val fontSize = 12 // Размер шрифта

var symbolPadding = 1.dp // Вертикальный отступ между символами

}

@Composable

fun MatrixBackground() {

Box(

modifier = Modifier.fillMaxSize().background(Color.Black)

) {

for (i in 0 until MatrixAnimationSettings.rows) {

MatrixColumn(MatrixAnimationSettings.symbols, i, MatrixAnimationSettings.fontSize)

}

}

}

@Composable

fun MatrixColumn(symbols: List<Char>, columnIndex: Int, fontSize: Int) {

var symbolList by remember { mutableStateOf(listOf<MatrixSymbol>()) }

var animationRunning by remember { mutableStateOf(true) }

val screenWidth = LocalConfiguration.current.screenWidthDp

val screenWidthPx = with(LocalDensity.current) { screenWidth.toInt() }

val randomXOffset = (Random.nextInt(1, 21) * 20)

val randomStartDelay = Random.nextLong(100L, MatrixAnimationSettings.maxDelay)

LaunchedEffect(Unit) {

delay(randomStartDelay)

while (animationRunning && isActive) {

delay(MatrixAnimationSettings.symbolDelay)

val newSymbol = MatrixSymbol(

symbol = symbols.random(),

index = Random.nextInt(0, 1000),

alpha = MatrixAnimationSettings.alphaStart,

yOffset = symbolList.size * 20,

xOffset = randomXOffset

)

symbolList = symbolList + newSymbol

symbolList = symbolList.mapIndexed { index, symbol ->

symbol.copy(alpha = symbol.alpha - MatrixAnimationSettings.fadeStep)

}

if (symbolList.size > MatrixAnimationSettings.maxVisibleSymbols) {

symbolList = symbolList.drop(1)

}

if (symbolList.all { it.alpha <= 0f }) {

animationRunning = false

symbolList = emptyList()

}

}

}

symbolList.forEach { symbol ->

Text(

text = symbol.symbol.toString(),

color = Color.Green.copy(alpha = symbol.alpha),

fontSize = fontSize.sp,

modifier = Modifier

.padding(MatrixAnimationSettings.symbolPadding)

.offset(x = symbol.xOffset.dp, y = symbol.yOffset.dp)

)

}

}

data class MatrixSymbol(

val symbol: Char,

val index: Int,

val alpha: Float,

val yOffset: Int,

val xOffset: Int

)

Показать полностью
19

Как скрыть изображение внутри другого?

*** Нет лучшей тайны, чем та, что у всех на виду ***

Увлекаюсь программированием и решаю нетривиальную задачку - как обеспечить отправку скрытых сообщений таким образом, чтобы они были как бы "двухслойные". Поверхностная беседа с мемчиками и скрытая, о которой знают только участники беседы. Например, вот этот мемчик, взгляните внимательно:

кажется мем немного зашакален, но на самом деле в каждом пикселе заменены 4 младших бита

кажется мем немного зашакален, но на самом деле в каждом пикселе заменены 4 младших бита

МЛАДШИЕ И СТАРШИЕ БИТЫ (LSB и MSB)

Биты — это самые маленькие единицы информации в цифровых изображениях, представляющие значения от 0 до 1. Каждый цветовой канал у каждого пикселя кодируется 8 битами.

Младшие биты это 4 бита пикселя, наименее влияющих на цвет пикселя. Старшие, соответственно, определяют цвет в большей степени. Мы помещаем эти биты в 4 младших бита основного изображения, делая старшие биты скрытого как бы младшими в основном, понижая их роль в изображении.

Если описать по операциям:

1. Очищаем младшие 4 бита в основном изображении.

2. Сдвигаем старшие 4 бита скрываемого изображения на 4 позиции вправо, чтобы оставить только значимые биты.

3. Объединяем основное изображение с преобразованными битами скрываемого изображения.

И вуаля. Вы отправили мемчик, посмеялись, а адресат смог извлечь скрытое изображение и получил ту информацию, которая не доступна при беглом анализе вашей переписки. Процесс расшифрофки до нельзя прост: разделеются биты каждого пикселя и остаются только старшие биты скрытого изображения. Оно чуть теряет в качестве, но основной контекст в нем совершенно читаемый. В мемчике в начале поста сокрыл своё фото, можете проверить как это работает.

Как проверить, если ты не программист и нет компилятора? Нужна любая нейросеть, работающая с контекстом изображений, например ChatGPT 4o with canvas.

Аналогичным образом можно скормить ей два изображения и попросить зашифровать одно в другое. Я сейчас работаю над приложением, в котором это будет происходить в полуавтоматическом режиме.

Аналогичным образом можно скормить ей два изображения и попросить зашифровать одно в другое. Я сейчас работаю над приложением, в котором это будет происходить в полуавтоматическом режиме.

Если тема интересна, то напишу постик например о стеганографии на основе преобразования Фурье, о шифровании (повсеместно используемый Advanced Encryption Standard) и о других интересных методах.

Показать полностью 2
Отличная работа, все прочитано!