Данная статья не является рекламной* , а служит больше социологическим опросом, автор не к чему не призывает. Важно узнать мнение и есть ли отклик у людей на политические и сатирические игры. Большая просьба дать комментарий находит ли отклик увиденное.
Memocratie- это сатирический кликер в котором высмеивается современная политика и так называемая "повесточка". В нем необходимо раздавать деньги "радужным" персонажам и игнорировать простых граждан.
Автор намеренно делает акцент на токсичных в игровой индустрии моментах, а именно политике, небинарных личностях и "LGBTQ+"
По механике нужно не только совершать клики, но еще и отделять зерна от плевел, так как это будет влиять на рейтинг игрока. Так же у игрока заканчиваются деньги в руках и необходимо совершать дополнительное действие для того чтоб достать новую пачку денег.
Чтоб игрок не заскучал, его будут развлекать забавные новостные вставки и необычные ачивки за достижения. Для тех, кто увлечется проектом в определенный момент условия игры поменяются и это изменит игровой процесс.
С прошлого года мы в Пикабу создали уже несколько собственных игр. Например, Пикман и Арканоид, который недавно крупно обновили. Мы не планируем останавливаться, потому что видим — каждая следующая игра по статистике привлекает всё больше пикабушников. Спасибо, что играете — для нас это лучшая награда.
Завершив работу над обновлением Арканоида, мы стали думать, какую же игру сделать следующей. Ответ очевиден, если вспомнить, что такое Пикабу. В первую очередь, это вы, пользователи. Так что самым правильным выбором будет выбор сообщества.
Кстати, в том же Арканоиде мы пошли по этому же пути: в продолжении за канон взяли ту концовку первой версии, которую выбрало большинство игроков.
Итак, у нас есть 5 претендентов на следующую игру Пикабу. Вот они слева направо сверху вниз:
1. Тетрис
Советская легенда, которая завоевала мир. Идея Алексея Пажитнова с 1985 года пораждает всё новые и новые переосмысления.
2. Space Invaders
Чтобы два раза не вставать с космической темы после Арканоида, можем сделать игру в другом классическом для аркад жанре — космический шутер.
3. Bomberman
Двоюродный брат Пакмана, который привык решать вопросы радикально — взрывчаткой.
4. Stack Attack
Популярная вариация механики Тетриса. Только здесь вы играете за грузчика, который умеет прыгать и двигать ящики. Ещё может получить ящиком по каске.
5. Сапёр
Головоломка, которая знакома всем с самых ранних версий Windows.
Люди, которые застали время становления компьютерных игр, подтвердят, что когда-то игры были «живыми». В то время, когда многие пропадали в игровых клубах, оставляя там деньги родителей, было много игр которые «живут» в этих людях до сих пор. Это просто романтика детства, скажите вы. Да, возможно.
Что привлекало в играх нас тогда? Что вообще заставляет человека играть?
Убить время.
Сегодня люди играют по пути домой или на работу, коротая время, томясь в ожидании. Кто-то читает книги, кто-то смотрит бесконечные ролики, кто-то читает новости, а кто-то играет в игры. В моем детстве времени у меня было много, и скоротать я его мог множеством способов. После школы можно было пойти гулять, играть в футбол или пойти на речку. Зимой можно было пойти на горку или каток. Можно было пойти к другу, послушать музыку, или пойти в гараж, поиграть на гитаре или барабанах, погулять с девушкой или другом, пойти на стройку или на заброшенный бетонный завод, кладбище, свалку машин, за большими подшипниками... Одним словом много вариантов. Но чаще мы шли в игровые клубы. Там была своя атмосфера, все там были своими. Причем там можно было встретить совершенно разную публику от забитого школьного тихони до отпетого уличного задиры. И цель посещения была точно не в убийстве времени.
Уйти от реальности.
Возможно, да. Но скорее нет. Как я уже сказал, там были совсем разные люди, разных возрастов. Возможно, кто-то искал там другую реальность, чтобы уйти от этой, только совсем не обязательно от того, что эта реальность его не устраивала. Просто та реальность была другой, не менее интересной.
Самореализация.
Кто-то не реализовал себя в жизни спорта, но всегда хотел этого, кто-то смотрел фильмы про войну и мечтал стать героем, кто-то хотел быть просто лучше других хоть в чем-то. Игровой клуб – место, где возможно все. Там ты можешь быть лучшим стрелком, или талантливым бойцом, там ты можешь быть умелым тактиком и стратегом, там можно проявить всю хитрость и смелость ведь при этом ты ничего не теряешь. Риск минимален, это же игра! Чувство победы с риском, конечно приятнее, но это не для всех.
Азарт и эмоции.
Самыми популярными играми тогда и сейчас являются игры для киберспорта. Азарт победы и эмоции, которые ты получаешь, занимая первое место сначала в клубе, затем в городе, а потом в другом городе... На самом деле нет разницы в чём ты лучший, главное, чтобы были те, кто это оценит. И чем их больше, тем ярче эмоции, тем слаще вкус победы.
Я думаю, можно найти ещё ряд причин, по которым так много людей играют в игры, но я хотел сказать ещё о другом моменте. Во что играют наши дети? В какие игры играют сегодня? Я понимаю, что их так много, но причины ведь те же не так ли? Давайте посмотрим на это не со стороны тех, кто играет, а со стороны тех, кто создает игры. Думаю, вы все, кто читает этот блог, скажите почти единогласно – игры создают, чтобы их продавать. А это значит, что тот, кто создает игру, должен завоевать как можно больше аудитории. Игра позволяет убить время, не надоедает, круто! Она увлекает играющего в другой мир, заставляя больше проводить в ней время, отлично! Дает возможность реализовать себя, класс! Взывает к чувству азарта, желанию вновь и вновь возвращаться к ней, чтобы не быть побеждённым, замечательно! Чем больше удалось воплотить в игре, тем она будет успешнее.
Со временем игры стали приносить огромные деньги. И на первое место стали выходить другие инструменты. Как заставить игрока потратить на игру как можно больше средств? Как получить больший доход, при минимальных затратах. Пришли экономисты и маркетологи. И игры стали терять, по моему мнению, самое главное, что в них было – душу, вложенную в них создателями. Стали терять идею, смысл, творческий след. Да они все такие же азартные, еще более реалистичные, в них все так же есть возможность самореализации, целые миры, другие реальности, поражающие воображение. Только для меня игры всегда были творческим произведением. Я, возможно, один из немного числа людей, кто видит в игре – произведение искусства. А возможно, таких как я много. Для меня игра это не просто, азарт, реальность, самореализация. Для меня игра – это то, что заставит тебя задуматься, возможно даже удивится. Игра, для меня, творение, несущее какую-то смысловую нагрузку. Дающее пищу для ума. Как, увлекшая тебя картина в галерее, или хороший фильм, или книга, игра должна оставить след в душе играющего. Может быть даже чему-то научить, изменить к лучшему. Она должна, обязана, быть «живой». Иначе, возникает чувство обмана, разве нет? В играх сегодня есть реклама, игровые покупки, шмотки, и многое что заставляет тебя тратить реальные деньги, не маленькие суммы к слову, чтобы играть дальше. Потому что нельзя сделать игру «живой», если цель ее зарабатывать деньги. Эти две вещи, к сожалению, не совместимы. Как только вступает в дело монетизация, жизнь постепенно угасает.
Сегодня появилась возможность делать игры простым инди – разработчикам. Появились игровые движки, образование, хорошие и доступные средства для разработки, создания игр. И многие авторы получили возможность «творить» игры. Как художники пишут картины, а писатели книги, как молодые режиссёры снимают картины, со своим видением мира, создатели игр, созерцатели, делают «живые» игры. Да, это не всегда получается у них. Но для них важен сам процесс, им просто необходимо излить свою внутреннюю энергию, рассказать о проблеме, попытаться что-то изменить.
Слова, слова, слова, скажите вы. Это всё вода, всё и так всем ясно. Одних слов мало, нужно подкреплять слова делом, не так ли? Я думаю, что так. Мы с другом детства всегда мечтали делать «живые» игры. Нам нравилось в них играть, и мы всегда хотели их создавать. Мы не великие программисты и художники. Мы простые самоучки. Просто взяли и сделали игру, и я думаю у нас получилось. Получилось сделать ее «живой». Без доната, рекламы, игровых покупок, без всей этой монетизации. Конечно, время стоит денег, но это простой проект двух разработчиков, поэтому деньги тут небольшие. Самое главное — это то, что мы сделали игру, нашу игру. И она получилась такой, какой, по нашему мнению, должна быть «живая» игра. В ней нет реалистичной графики или эффектов, но есть свой неповторимый стиль, увлекающий, и по-своему прекрасный. В ней есть сюжет и идея. В ней есть вызов, тактика и стратегия. Есть разнообразность миссий и противников, боссов и миров, оружия и способностей. Это РПГ, как тогда, когда мы играли в такие игры. Такая игра, как те, что мы любили в детстве. И все те, кто разделял с нами удовольствие именно от таких «живых» игр, я уверен вспомнят его и с нашей. Снова переживут эти эмоции. Снова окунутся в атмосферу теперь уже ретро, РПГ аркад. Во всяком случае мы были бы этому очень рады, и очень бы этого хотели. Ведь именно для этого мы ее и «родили».
Если у меня получилось вас заинтересовать, то посетите нашу группу.
Не оставайтесь равнодушны и к этому посту.
А если вы готовы поддержать нашу команду, бросить нам вызов и пройти нашу игру, готовы пережить настоящие эмоции от классической «живой» РПГ аркады можете найти нашу игру для Android - NovaSpace (retro RPG) в магазинах AppGallery или RuStore.
От маленького зернышка до мягкой пены в бокале — рассказываем, как мы готовим безалкогольное пиво!
Вот так выглядят здоровые колосья, из которых мы получаем зерна для безалкогольного пива
Этап 1. Подготовительный
Мы начинаем с отбора семян
Из них вырастет ячмень. Из ячменя будет сделан солод, из которого мы приготовим безалкогольное пиво. Чтобы получить большой хороший урожай, важно выбрать правильные сорта. Наши сорта «Грэйс», «Гузель», «Евгения» и «Фатима» устойчивы к болезням, вредителям и внезапным майским похолоданиям :) Из таких семян вырастут крупные колосья с большим количеством зерен.
И выбираем место для посевов
Ячмень — это не петербуржец! Ему важно солнце и чтобы температура была выше 10 градусов минимум 150 дней в году. Такие условия есть в 17 регионах России, включая Тульскую, Рязанскую, Липецкую и Воронежскую область, а также Приморский край. Для своих полей мы выбираем земли, которые богаты черноземом — самой плодородной почвой.
Также почву нужно подготовить
В лаборатории мы тестируем почву на уровень кислотности и других показателей, которые влияют на растения. Это помогает подобрать правильные экологичные удобрения, которые ускоряют рост зерна и повышают урожайность. После этого можно переходить к посеву.
Этап 2. Выращивание и сбор урожая
Контроль на каждом этапе роста
Спутниковые снимки ячменных полей «Балтики»
Разве может человек уследить за полями в несколько тысяч гектаров? Может, если на помощь ему приходят современные технологии! Мы наблюдаем за ростом ячменя со спутника: каждую неделю наши сотрудники получают фотоотчет, на котором виден прогресс.
А вот так выглядит созревший урожай — можно собирать!
Сбор и хранение
Урожай собирают с помощью уборочных комбайнов и отправляют на хранение в элеваторы. Это здания с подходящей температурой и влажностью, чтобы зерно в них не испортилось. В одном таком элеваторе помещается до 10 000 тонн зерна!
Элеватор для хранения изнутри, на фото сотрудница открывает специальный люк для проветривания
Этап 3. Соложение
Это когда зерно превращается в солод. Из элеваторов ячмень поступает на солодовню. Здесь он проходит контроль качества, очистку. Далее зерна замачивают в воде в течение нескольких циклов. Затем ячмень проращивают в специальных ящиках — обычно на это уходит от пяти дней. Из этих ростков вполне может развиться новое растение. Но это не наша цель! Поэтому отправляем пророщенный ячмень на сушку, а если нам нужно получить карамельный солод, то на обжарку. Теперь — это солод!
Солод для безалкогольного пива — то же самое, что виноград для вина или яблоки для сидра. От условий сушки и обжарки зависит, какой цвет, аромат и вкус будет у готового напитка.
Этап 4. Варка
Готовый солод отправляется в варочный цех. Здесь его измельчают, смешивают с водой, нагревают, фильтруют и кипятят. На этапе кипячения в получившуюся смесь добавляют хмель для вкуса и аромата.
Добро пожаловать в варочный цех!
Для приготовления безалкогольного пива часто используют два вида хмеля: горький и ароматический. Их добавляют в начале и в конце кипячения соответственно.
Этап 5. Брожение
Думаете, что после варки безалкогольное пиво уже готово? А вот и нет! Далее оно отправляется в бродильный цех, в котором расположены цилиндро-конические танки из нержавеющей стали. Там в будущий напиток добавляют дрожжи. Процесс брожения б/а пива занимает от 7 до 14 дней в зависимости от вида дрожжей. Но получившийся продукт — это еще не безалкогольное пиво. Перед тем, как попасть в бутылку, напиток должен созреть.
Вот так выглядят бродильные танки
Этап 6. Созревание
Этап особого творчества для пивоваров, когда будущее безалкогольное пиво приобретает свой окончательный вкус и аромат. На этом этапе за счет использования специальных дрожжей мы получаем безалкогольное пиво, в котором присутствует менее 0,5% спирта.
Этап 7. Фильтрация
Как только безалкогольное пиво созрело, приступаем к фильтрации. Это многоуровневый процесс. Сначала напиток попадает в сепаратор, где отделяются крупные взвеси и дрожжи. Далее проходит через кизельгур — фильтрующий материал, который помогает хорошо отделить оставшиеся дрожжи и белковую взвесь. Последний стадия — тонкая фильтрация. В результате фильтрации пиво осветляется, становится прозрачным. По окончании фильтрации безалкогольное пиво поступает в специальные накопительные емкости — форфасы. Здесь оно ожидает своей очереди поступления на линию розлива.
Нефильтрованное безалкогольное пиво не проходит стадию фильтрации и содержит небольшое количество дрожжей, придающих характерные особенности вкусу напитка.
Этап 8. Розлив
Напиток готов! Теперь его можно разливать по упаковкам. Но, перед тем как разлить, в зависимости от сорта, следует процесс пастеризации. Это кратковременный нагрев, который замедляет жизнедеятельность микроорганизмов, чтобы увеличить срок годности напитка.
Для розлива мы используем стеклянные бутылки, алюминиевые банки и кеги для ресторанов. А еще сами производим ПЭТ бутылки из компактных заготовок. Упаковка перед розливом проверяется специальным оборудованием — инспекторами. Брак не пройдет!
Выбор упаковки не влияет на вкус напитка: один сорт может разливаться по бутылкам и банкам
Теперь вы знаете больше о пивоварении! Ищите безалкогольную «Балтику» в любимых магазинах и на маркетплейсах.
Реклама ООО «Пивоваренная компания «Балтика»» 18+, ИНН: 7802849641
Дисклеймер: игра была написана как простенькая, но познавательная демка именно для PowerVR MBX и именно для Axim X51v. Именно поэтому здесь нет нормального Update-таймера, расчёта дельты времени, а игра прибита к константным временным отрезкам и величинам скорости!
Итак, как же игры подобного планы работают «под капотом»? По факту, обычно мы с вами никуда не едем: фоновые модели ландшафта и дороги просто скроллятся и телепортируются друг за другом, когда одна из частей уходят за экран, что создаёт эффект бесконечной дороги. И эта техника используется во многих играх! Что же касается машинок, от которых мы должны лавировать, то это не мы едем на них, это они едут на нас! По итогу создаётся эффект будто мы с вами куда-то едем и уворачиваемся от машинок, хотя на деле это не так!
Начинаем с реализации базовой вещи в архитектуре любой современной игры, а именно системы игровых объектов. В нашей игре нет необходимости в реализации сложного графа сцены с комплексной компонентной системой, или, например, ECS. Хватит классического линейного списка игровых объектов (который использовался, например, в Half-Life), по которому объект World проходится каждый кадр, вызывая необходимые функции для обновления состояния объекта и его отрисовки:
public abstractclass Entity { public Transform Transform;
foreach (Entity ent in entityRemovalList) Entities.Remove(ent);
entityRemovalList.Clear(); }
publicvoid Draw() { sky.Draw();
renderer.Draw();
foreach (Entity ent in Entities) ent.Draw(); }
Самым первым нашим объектом будет машинка игрока, которой можно будет управлять! Модельки я взял лоуполи со скетчфаба, вот ссылка на ВАЗ 21099 и VW Golf Mk2. Спасибо авторам моделей за их работу!
Наследуемся от Entity и реализуем абстрактные методы с логикой объекта. Здесь мы получаем состояние аппаратных кнопок влево и вправо, в зависимости от них вычисляем направление поворота машинки и, собственно, поворачиваем машинку путём сложения с координатой X вычисленного направления, помноженного на «скорость» поворота машинки. Для лучшего визуального эффекта, мы также плавно поворачиваем машинку эффектом а-ля EaseIn/EaseOut:
Теперь нам нужно, чтобы машинка где-то «ездила». Для этого мы моделируем в блендере примитивный кусок дороги с элементами ландшафта:
А затем реализуем примитивный рендерер фона, который будет скроллить два одинаковых seamless-куска уровня и как я уже говорил ранее, просто телепортировать их друг за другом, создавая эффект бесконечности.
public SectorRenderer() { road = Model.FromFile("road.mdl"); roadMaterial.Diffuse = Texture2D.FromFile("road.tex");
Где terrain.mdl — окружающий ландшафт, а road.mdl — собственно, сам меш дороги. Получаем вот такой эффект:
Артефакты на видео — следствие проблем с точностью float у MBX Lite в процессе клиппинга геометрии при ближней плоскости отсечения в 0.1f. Меняем на 1.0f и всё снова работает нормально :) Чуть изменяем проекцию, переместив камеру выше и наклонив на 45 градусов и игра уже похожа на Traffic Racer!
Переходим к реализации машин трафика. Модельки их машин будут загружаться при старте игры:
publicstaticvoid Preload() { PreloadedCars = new Model[1]; PreloadedMaterials = new Material[1];
LoadTrafficModel(0, "traffic1"); }
А сама их логика предельно проста. При спавне, машинка выбирает себе полосу, по которой будет ехать и рандомный множитель скорости, который вносит разнообразие в игру:
Переходим к обработке столкновений. Помним, что мы на этапе конвертации моделей посчитали Axis Aligned Bounding Box для каждой модели? В качестве алгоритма мы будем использовать классический AABB — или Rect vs rect:
public bool Intersects(BoundingBox box) { return (X < box.X + box.X2 && Y < box.Y + box.Y2 && Z < box.Z + box.Z2 && box.X < X + X2 && box.Y < Y + Y2 && box.Z < Z + Z2); }
Теперь для проверки столкновения между ними, нам надо посчитать абсолютный Bounding Box для каждого игрового объекта:
Затем итерируемся по списку всех игровых объектов в сцене, и если у нас есть машинка трафика, то проверяем на столкновение с машинкой игрока. Если столкнулись, то помечаем машинку игрока как разбитую и предлагаем игроку рестартнуть игру.
foreach (Entity ent in Game.Current.World.Entities) { if (ent is TrafficCar) { if (Player.Bounds.Intersects(((TrafficCar)ent).Bounds)) { // TODO: Damage logic Player.IsDestroyed = true; } } }
Уже что-то немного похожее на игру. Добавим конечное препятствие — необходимость рестарта при столкновении с другой машинкой и для демки пока-что хватит.
if (Game.Current.world.Player.IsDestroyed) { int measure = Engine.Current.Graphics.MeasureString(RestartString); Engine.Current.Graphics.DrawString("Press Return to restart", Engine.Current.Graphics.ViewWidth / 2 - (measure / 2), Engine.Current.Graphics.ViewHeight / 2, StatsColor); } }
Вот что у нас получилось:
Правда, что на МКАДе каждый вечер такое? Я просто не с МСК :)
❯ Заключение
Вот такой у нас получился материал про PowerVR MBX! С выходом iPhone, этот GPU дал толчок для появления красивых мобильных игр с уровнем графики, близким к полноценным домашним консолям… жаль, что золотая эра интересных, самодостаточных и бездонатных мобильных игр и закончилась во времена iPhone 5 :(
В остальном же, надеюсь материал был достаточно интересен и познавателен для всех моих читателей, даже тех, кто никогда не программировал игры! Был у вас Dell Axim X51v? Пишите в комментариях!
Исходный код демки и бинарники можно найти на моём гитхабе.
Материал написан при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud , чтобы не пропускать новые статьи каждую неделю! А ещё у меня есть своя телега, куда я публикую бэкстейдж статей и вовремя публикую ссылки на новый материал!
Понравился материал?
А ещё я собираю деньги на проект с уже настоящим, физическим ТАЗом и его электронным дооснащением бортовым компьютером "по самому дешману" своими руками! Уже собрано 50.000 рублей из планируемых 70.000 на машину, из них 45.000 моих личных сбережений и 5.000 рублей - помощь читателей, за что вам большое спасибо :)
Процесс создания игры практически так же увлекателен, как играть в игру)
Только вместо режима "хардкор" играем на "мегахардкор".
ландшафт уровня с миром тёмных протоссов
Заморочился сделать мини-катсцены, где что-то происходит и персонажи говорят свои реплики. Казалось, реализовать это несложно, но на деле пришлось повозиться. То диалоги выскакивали невовремя, то не ставилась пауза, то не срабатывали триггеры... Но, с горем пополам и кучей костылей в коде удалось создать даже некоторое подобие системы.
список персонажей, условий и их реплик
Просто текст показался скучным, поэтому решил - персонажам нужны голоса. Много времени потратил на поиск нейросети для озвучки текста, но результат работы таких сеток очень плохой для озвучки игры. После того, как разочаровался в искусственных интеллектах, просто включил микрофон и озвучил героев самостоятельно 😬.
С добавлением новых персонажей, добавились новые механики. Так, научил сталкеров варпаться, прицеливаться и стрелять, с развитием зилотов появились энергетические щиты, зелёная разновидность тараканов теперь заражает спорами врага, который после смерти плодит труплинга, червь нидуса вылазит и изрыгает.
явление сталкеров зерглингам
дезинсекция
Наконец-то заработало меню игры! Для этого пришлось переписать передвижение персонажа с нуля. Потому что в стандартных ассетах контроллера от первого лица применены какие-то модные скрипты, которые не хотели отдавать управление курсором добровольно.
Один и тот же ствол со временем приедается, но добавлять новые виды оружия пока что облом. Поэтому пушка одна и та же. Потом добавил ей небольшой баф, убивающий мелких юнитов ваншотом) И чтобы одна и та же волына не надоедала, сделал оружие массового урона, бьющее сплэшем по области.
плюх в ручеёк из зерглингов
В общем, добавил в игру протоссов, злых протоссов, больших зергов, тёмный мир, потоки зергов, сюжет. На данный момент есть 2 полноценных сцены, связанные по смыслу, третья наполовину готовая и четвёртая, заключительная.
Надеюсь, скоро получится собрать играбельную демку)
Сделал видео трейлер:
А пока подумаем, как назвать изделие) Первое, что пришло в голову, это сделать номерную часть №10, так как выход её с такой скоростью разработки не скоро) Или просто буква Х. Ну или свой вариант напишите, фантазия на этом моменте остановилась.
Если сейчас приехать в пункт приема металлолома, то можно обнаружить просто огромные кучи различных телефонов и прочих электронных «отходов», которые стоят под открытым небом и ждут, когда придёт их черёд окончательного разложения. Однако при ближайшем рассмотрении выясняется, что многие девайсы оказываются полностью рабочими даже после недельного лежания под палящим солнцем и проливными дождями, а сдали их в чермет по причинам «не нужен, надоел, купил новый» и т. п. Я не считаю это правильным, ведь даже в простые кнопочные звонилки имеется возможность вдохнуть новую жизнь, если знать один интересный, но малоизвестный факт: для них можно писать нативные приложения на C и использовать железо телефона в своих целях. А это, на минуточку, как минимум: дисплей с подсветкой, вибромотор, динамик, клавиатура и GSM-радиомодуль с возможностью выхода в сеть. Сегодня мы с вами: узнаем, на каких аппаратных платформах работают китайские телефоны, какие существуют программные платформы и где взять для них SDK, а в практической части мы напишем 2D-игру с нуля, которая будет работать на многих китайских кнопочниках. Интересно? Тогда жду вас под катом!
Содержание:
Не J2ME едины
Аппаратные ресурсы
Кроссплатформенный рантайм
Кроссплатформенный рантайм: Win32
Кроссплатформенный рантайм: MRE
Кроссплатформенный рантайм: VXP
Наконец-то пишем игру
Тестируем на реальных девайсах
Заключение
❯ Не J2ME едины
Думаю, многие мои читатели помнят о такой платформе, как J2ME. Java-приложения стали фактически основной возможностью расширения функционала телефонов в 2000-х годах. API для них был достаточно хорошо стандартизировано, программы не зависели от архитектуры процессора и ОС устройства, а порог вхождения для написания собственных приложений был довольно низкий и даже новички могли за пару дней написать свою игрушку или какое-нибудь GUI-приложение!
Однако не одним J2ME мы были едины: существовало множество платформ, которые так или иначе пытались занять нишу Java на рынке. Некоторые из них я упоминал в своей прошлой статье о написании 3D-игры под Sony Ericsson с нуля: например, была такая платформа на телефонах Sony Ericsson серии T, как Mophun, а CDMA-телефонами с чипсетами Qualcomm использовалась нативная платформа BREW. Пожалуй, я не буду упоминать о .sis и .cab — поскольку это форматы нативных приложений для смартфонов, а не простых «фичефонов».
В какой-то момент, ближе к 2006-2007 году, прилавки российских официальных ритейлеров (по большей части это были телефоны Fly) и неофициальных продавцов на рынках заполонили различные китайские телефоны, которые предлагали какой-то немыслимый функционал для тех лет за копейки, да ещё и визуально напоминали флагманские модели известных брендов. Пожалуй, одним из самых популярных таких телефонов была Nokla TV E71/E72 (да, именно «нокла»), вышедшая примерно в 2008 году и производившаяся аж до 2011 года! За 2-3 тысячи рублей (это менее 100 баксов), пользователь получал здоровый 2.4" дисплей с разрешением 240x320 весьма неплохого качества (когда в те годы многие продолжали ходить с 176x220), да ещё и с тачскрином, гироскоп, огромный громкий динамик (пусть и не очень качественный), поддержку SD-карточек до 32Гб, нередко фронтальную камеру, а также премиальный дизайн с вставками из алюминия. Частенько китайцы заботливо клали в коробку ещё чехольчик и дополнительный аккумулятор :)
Были даже полные копии существующих устройств от Nokia. Особенно китайцы любили подделывать массовые модели на S40: они были очень популярными и китайцы хотели откусить свой кусок рынка у Nokia. Пусть и рынка серого импорта — очевидно, в салонах связи подделки никто не продавал:
Но была и ложка дёгтя в этой бочке меда: китайские телефоны очень часто не имели поддержки Java, из-за чего многие пользователи разочаровывались в них из-за отсутствия возможности установить необходимые им приложения. Никакой тебе оперы, аськи, игр… Скорее всего, это связано с необходимостью отчислений Sun, а также разработчикам реализации J2ME-машины (JBed/JBlend) и установки чипа флэш-памяти чуть большего объёма.
Но многие пользователи не знали, что такие девайсы не просто поддерживали сторонние приложения, но и умели выполнять настоящие нативные программы, написанные на полноценном C! Всему помешала китайская костыльность и тотальная закрытость. Платформа предполагалась для работы на внутреннем рынке. Для вызова менеджера нативных приложений необходимо было вводить специальный инженерный код в номеронабирателе, предварительно скопировав приложение в нужную папку, а SDK долгое время было платным и доступно только для компаний из Китая. Кроме того, далеко не все приложения могли запустить на конкретном девайсе — были серьезные проблемы с совместимостью.
Всё как вы любите: HiTech-девайсы на фоне ковра, который старше автора лет на 30 :)
В ранних китайских телефонах использовалась платформа Mythroad (MRP, MiniJ) от китайской компании SkyWorks, которая лицензировала свою технологию производителям чипсетов. Поддержку MRP можно было встретить на телефонах с чипсетами MediaTek, Spreadtrum, а также MStar (и возможно Coolsand). Mythroad предоставлял некоторое API для работы с железом телефона и разработки как UI-приложений, так и игр, кроме того, Mythroad позволял хранить ресурсы в одном бинарнике с основной программой и даже имел какой-то интерпретируемый язык помимо возможности запуска нативного кода. Для работы таких приложений необходимо было скопировать менеджер приложений dsm_gm.mrp и игру в папку mythroad во внутренней памяти устройства или на флэшке, а затем набрать в номеронабирателе код *#220807#, иногда при отключенной первой SIM-карте. Костыльно? Костыльно! Откуда об этом знать среднестатистическому пользователю? Не откуда! Но работало!
Эта платформа поддерживалась на большинстве подделок под брендовые устройства Nokia, Sony Ericsson и Samsung, а также iPhone и на многих китайских кнопочных телефонах 2008-2010 годов.
Ближе к 2010 году MediaTek разработала свою собственную платформу, которая должна была заменить MRP — WRE (VXP). Эта платформа была гораздо шире с точки зрения функционала (например, был доступ к UART) и её API был вполне удобно читаем для программиста, а SDK свободно доступен для всех. Один нюанс всё портил — приложения без подписи привязывались к IMSI (даже не IMEI) симки в девайсе и на некоторых девайсах требовали переподписания под каждую конкретную SIM или патчинг дампа оригинальной прошивки телефона на отключение проверки подписи. Эта платформа поддерживалась на многих кнопочниках и смарт-часиках 2010-2020 годов: к ним относятся новодельные телефоны Nokia, телефоны DNS и DEXP, Explay и т. п. Для запуска приложений достаточно было выбрать файл с разрешением VXP в проводнике и просто запустить его. Но с совместимостью всё равно имелись проблемы: если запустить VXP для версии 2.0 и выше, мы получим лишь белый экран. Ну хоть не софтресет, и на том спасибо!
Далеко не все такие часы поддерживают MRE, смотреть нужно от устройства к устройству
❯ Аппаратные ресурсы
Большинство китайских кнопочных телефонов работает на базе одних и тех же чипсетов. В конце нулевых чаще всего использовались чипсеты MT6225, SC6520 и некоторые чипы от Coolsand. Средние хар-ки девайса были следующими:
Процессор: ARMv5 ядро на частоте ~104МГц, ARM926EJ-S. Нет FPU, есть Thumb. Большую часть процессорного времени программа могла забрать себе.
ОЗУ: ~4Мб SDRAM. Программам было доступно 512Кб-1Мб Heap'а. Это, в целом, довольно немало для большинства применений.
Флэш-память: ~32Мб, пользователю доступно пару сотен килобайт. Да, вы не ослышались, килобайт! Однако можно без проблем использовать MicroSD-флэшки до 32Гб.
Дисплей: от 128x128 до 320x480, почти всегда есть 18-битный цвет (262.000 цветов), в случае TV E71/E72 используется очень неплохая TN-матрица с хорошими углами обзора и яркой подсветкой. Иногда есть тачскрин.
Звук: громкий динамик, наушники.
Аккумулятор: ~800мАч, на некоторых девайсах может быть и 2.000мАч, а то и больше!
Ввод: клавиатура, иногда была поддержка QWERTY.
Внешние шины: почти всегда был доступен UART, причём его можно было свободно взять прямо с платы — он был явно подмечен! Взять GPIO с проца не выйдет (кроме, возможно, вибромотора), SPI и I2C также напрямую недоступны. Внешние шины можно реализовать с помощью UART через GPIO-мост из микроконтроллера.
В итоге мы получаем очень неплохие характеристики для устройства, которое сочетает в себе сразу всё. На базе такого девайса можно сделать и сигнализацию, и HMI-дисплей с интерфейсом для управления каким-нибудь устройством, и игровую консоль с эмуляторами… да на что фантазии хватает! И это за какие-то 200-300 рублей, если мы говорим о б/у устройстве или 600 рублей, если говорим о новом. Это дешевле, чем собирать девайс с подобным функционалом самому из готового МК (например, RP2040) и отдельных модулей. Кстати, дешевые 2.4" дисплеи на алике — это ни что иное, как невостребованные остатки дисплеев для подобных китайских телефонов на складах! А вы думали, откуда там значки на тачскрине снизу?
Однако в рамках данной статьи мы не будем ограничиваться лишь теорией и на практике напишем примитивную 2D-игрушку, которая будет работать сразу на трех платформах без каких-либо изменений в коде самой игры: Windows, MRP (Mythroad) и VXP. Но для того, чтобы достигнуть такого уровня абстракции от платформы, нам необходимо написать рантайм, который оборачивает все необходимые платформозависимые функции для нашей игры.
Игрушка будет простой: 2D скролл-шутер с видом сверху, а-ля Asteroids. Летаем по космосу, и стреляем по враждебным корабликам, стараясь не попасть под вражеские лазеры. Всё просто и понятно :)
❯ Практическая часть: Кроссплатформенный рантайм
Итак, что нам необходимо от абстракции для такой простой игры? Давайте посмотрим:
Графика: очистка экрана, отрисовка спрайтов с прозрачностью (без альфа-блендинга, только колоркей), отрисовка текста. При возможности, желательно использовать нативное API системы для рисования графики, а не городить собственный блиттер. Формат пикселя фиксирован: RGB565 (65к цветов).
Ресурсы: хранятся в одном образе с основной игрой. Фактически, все ресурсы упакованы в виде обычных массивов байт в заголовочных файлах. Я пользуюсь вот этой тулзой для конвертации спрайтов в массивы байтов.
Звук: воспроизведение хотя-бы одного WAV-потока. Почему одного? Потому что далеко не на всех платформах есть доступ к аппаратному микшеру… да и вообще не везде есть прямой доступ к PCM (привет MRP), иногда разработчики ограничиваются лишь одним каналом для WAV-звука без возможности воспроизведения нескольких аудиофайлов одновременно.
Ввод: абстракция от клавиатуры классического моноблока: стрелки, OK, левый и правые софткеи.
Стандартная библиотека: не на всех платформах можно вызывать функции напрямую из stdlib. Как минимум в MRP и, например, «эльфах» для Motorola, нет возможности вызывать аллокатор, rand и некоторые другие функции из обычных заголовочников стандартной библиотеки. На таких платформах, системные инклуды дефайнами подменяют стандартные функции на своих реализации:
#define malloc system_alloc
#define free system_free
Но если у нас игра кроссплатформенная, то и платформозависимые инклуды мы использовать не будем.
Выглядит всё достаточно просто, верно? Примерно такого набора функций хватит для нашей игры:
❯ Win32
Давайте же перейдем к реализации рантайма на каждой платформе по отдельности. Начнём с Win32, поскольку адекватно отлаживать игру можно только на ПК.
На десктопе у нас будет фиксированное окно 240x320, в качестве GAPI будет использоваться аппаратно-ускоренный OpenGL, а для обработки ввода будет использоваться классически GetAsyncKeyState. Реализация точки входа, создания окна и инициализации контекста GL и главного цикла приложения у нас такая:
Реализация отрисовки спрайтов очень примитивная — OGL 1.0, полностью FFP, вся отрисовка — это 2 треугольника, формирующие квад. Спрайт заливается при первом использовании в текстуру, последующие кадры реюзается уже готовая текстура. Фактическая реализация всего рендерера — т. е. функций для рисования «просто картинок», без поддержки атласов, блендинга цветов (З.Ы - длинные листинги будут на пастбине, на Пикабу нет нормального тега для кода):
С вводом тоже всё просто. Есть биндинг кнопок клавиатуры к кнопкам на кейпаде телефона. inGetKeyState предполагается вызывать один раз за кадр, поэтому функция опрашивает ОС о состоянии нажатых кнопок на клавиатуре и назначает состояние виртуальных кнопок относительно состояния физических кнопок на клавиатуре.
Результат:
❯ MiniJ
Переходим к реализации рантайма для первой китайской платформы — MRP. Обратите внимание — я использую нативное API платформы для рисования спрайтов. Связано это с тем, что софтварный блиттер работает невероятно медленно даже с прямым доступом к скринбуферу устройства, а в чипсете предусмотрена отдельная графическая подсистема с командбуфером для быстрой отрисовки примитивов и графики:
SDK для MRE можно найти здесь (SKYSDK.zip): оно уже пропатчено от необходимости покупки лицензии. MRP не развивается более 10 лет, поэтому, думаю, его можно считать Abandonware. Компилятор находится в compiler/mrpbuilder.NET1.exe. За китайские SDK в публичном доступе нужно поблагодарить пользователя 4pda AjlekcaHgp MejlbHukoB, который раздобыл их на всяких csdn и выложил в свободный доступ :)
У MRP собственная система сборки, основанная на конфигурациях. Поскольку MRP может работать на устройствах с разными платформами и размерами дисплеев, под каждую можно настроить свой конфиг, который пережмет ресурсы в нужный формат. Дабы ничего не ломать, я заюзал абсолютные пути:
Компиляция приложения:
mrpbuilder.net1.exe game.mpr
Начинаем с функций обработки событий и инициализации, которые вызывает рантайм при старте приложения: mrc_init вызывается при старте приложения, а mrc_event при возникновении события. Вся инициализация очень простая: создаём таймер для обновления и перерисовки состояния игры и вызываем инициализацию игры:
С вводом тоже никаких проблем нет, нажатия кнопок прилетают как события в mrc_event. Переводим кейкоды MRE в наши кейкоды и сохраняем их состояние:
Опять же, отлаживать MRP-приложение под реальным устройством проблематично, поэтому платформозависимый код должен быть минимальным. Кроме того, обратите внимание, что некоторые функции в MRP зависят от библиотек-плагинов. Линкер слинкует вашу программу, но на реальном устройстве их вызов вывалится в SIGSEGV и софтресет устройства. Также нельзя использовать ничего из стандартной библиотеки именно в стандартных заголовочниках (т. е. stdlib.h, string.h и т. д.), часть стандартной библиотеки реализовывается MRP и дефайнится в mrc_base.h
Что интересно, защиты памяти толком нет. Если приложение падает в SIGSEGV или портит память — систему, судя по всему, ребутит Watchdog. Защиты памяти никакой, можно напрямую читать и писать в память ядра, а также писать в регистры периферии чипсета. jpegqs, покумекаем над этим? :)
Переходим к рендереру. Тут буквально две функции, gClearScreen очищает экран, а gDrawBitmap рисует произвольный спрайт с форматом пикселя RGB565. В качестве ROP используется BM_TRANSPARENT — таким образом, mrc_bitmapShowEx будет использовать левый верхний пиксель в качестве референсного цвета для реализации прозрачности без альфа-блендинга.
voidgDrawBitmap(CBitmap* bmp, int x, int y) {
mrc_bitmapShowEx((uint16*)bmp->pixels, x, y, bmp->width, bmp->width, bmp->height, BM_TRANSPARENT, 0, 0);
}
Да, всё вот так просто. Рантайм теперь запускается на реальных китайских девайсах и работает стабильно.
❯ VXP
Теперь переходим к VXP — платформе не менее неоднозначной, чем MRP. Пожалуй, начать стоит с того, что VXP существует аж в трёх версиях: MRE 1.0, MRE 2.0 и MRE 3.0. В MRE 2.0 и выше появилась поддержка плюсов (в MRE 1.0 только Plain C) и довольно интересного GUI-фреймворка, MRE 1.0 же предлагает реализовывать гуй самому. Платформа распространена на большинстве кнопочных телефонов и смарт-часиков на чипсетах MediaTek, примерно начиная с 6235 и заканчивания 6261D. SDK можно скачать вот здесь (см MRE_SDK_3.0).
VXP сам по себе более функционален чем MRE, поскольку ориентирован исключительно на телефоны с чипсетами MediaTek. Но что самое приятное — есть доступ к уарту без каких либо костылей! То есть, если сделать GPIO-мост на условной ESP32, то мы можем получить готовый мощный МК с клавиатурой, кнопками, дисплеем, звуком и т. д. Звучит не хило, да? Кроме того, у нас есть доступ и к BT, и к GPRS, и к SMS без каких либо ограничений.
Однако в бочке мёда нашлась и ложка дёгтя: для компиляции MRE-приложений необходимо накатывать и крякать довольно старый компилятор ADS, который сам по себе поддерживает только C89 (например, нет возможности объявить переменную в объявлении цикла или середине функции, только в начале, как в Pascal). ADS уже вроде как Abandonware, так что это вроде не наказуемо… но всё равно неприятно.
Кроме того, на некоторых девайсах (в основном, фирменных Nokia а-ля 225), прошивка требует подписи у всех бинарников, либо если бинарник отладочный, то должна быть привязка к конкретному IMSI.
К тому же, каждая программа должна фиксированно указывать в заголовке, сколько Heap-памяти ей необходимо выделить. Оптимальный вариант — ~500Кб, тогда приложение запустится вообще на всех MRE-телефонах.
Зато у VXP есть адекватный симулятор под Windows. Но зачем он нам, если у нас порт игры под Win32 есть? :)
Начинаем с инициализации приложения. В процессе вызова точки входа, приложение должно назначить обработчики системных событий, коих бывает несколько. Для обработки ввода и базовых событий хватает всего три: sysevt (события окна), keyboard (физическая клавиатура. Есть полная поддержка QWERTY-клавиатур), pen (тачскрин).
Переходим к обработчику системных событий. Обратите внимание, что MRE-приложения могут работать в фоне, из-за чего необходимо ответственно подходить к созданию и освобождению объектов. Что важно усвоить с самого начала — в MRE нет понятия процессов и защиты памяти, как на ПК и полноценных смартфонах. Любая программа может попортить память или стек ОС, более того, программа использует аллокатор остальной системы, поэтому если ваша программа не «убирает» после себя, данные останутся в памяти со временем приведут к зависанию. Впрочем, WatchDog делает свою работу быстро и приводит телефон в чувство (софтресетом) за 1-2 секунды. Но как и в случае с MRE, есть приятный бонус: прямой доступ к регистрам чипсета :)
Переходим к обработке событий с кнопок. Тут всё абсолютно также, как и на MRE, лишь имена дейфанов поменялись :)
И наконец-то, к графике! Пожалуй, стоит сразу отметить, что более 20-30 FPS на большинстве устройств вы не получите даже с прямым доступом к фреймбуферу. Похоже, это связано с тем, что в MRE довольно замороченная графическая подсистема с поддержкой альфа-канала (только фиксированного во время вызова функции отрисовки картинки/примитивов, сам пиксельформат всегда RGB565) и нескольких слоев. Кроме того, похоже есть ограничения со стороны контроллера дисплея.
Изначально, MRE предполагает то, что все картинки в программе хранятся в формате… GIF. Да, весьма необычный выбор. Однако для работы с пользовательской графикой, есть возможность блиттить произвольные картинки напрямую из RAM. Вот только один нюанс — посмотрите внимательно не объявление следующей функции:
dst_disp_buf — это целевой RGB565-буфер. Логично предположить, что и src_disp_buf — тоже обычный RGB565-буфер! Но как бы не так. Документация крайне скудная, пришлось посидеть и покумекать, откуда в обычном 565 буфере возьмется индекс кадра. С подсказкой пришёл пользователь 4pda Ximik_Boda — он скинул структуру-заголовок, которая идёт перед началом каждого кадра. В документации об этом не сказано ровным счетом ничего!
Сначала я реализовал софтовый блиттинг, но он безбожно лагал. Мне стало интересно, почему нативный blt быстрее и… вопросы отпали после того, как я поглядел в ДШ чипсета: тут есть аппаратный блиттинг. И даже с ним девайс не может выдать более 20FPS!
Для реализации более-менее шустрого вывода графики, необходимо сначала создать канвас (фактически, Bitmap в MRE), создать и привязать к нему layer, получить указатель на буфер слоя и только потом скопировать туда нашу картинку. Да, вот так вот замороченно:
И только после этого всё заработало достаточно шустро :) В остальном же платформа довольно неплохая. Да, без болячек не обошлось, но всё же перспективы вполне себе есть.
На данный момент, этого достаточно для нашей игры.
❯ Пишем геймплей
Рантайм у нас есть, а значит, можно начинать писать игрушку. Хоть пишем мы на Plain-C, я всё равно из проекта в проект использую +- одну и ту же архитектуру относительно системы сущностей, стейтов и т. п. Поэтому центральным объектом у нас станет CWorld, который хранит в себе на пулы с указателями на другие объектами в сцене, а также игрока и его состояние:
Система стейтов простая и понятная — фактически, между состояниями передавать ничего не нужно. При нажатии в главном меню на «старт», нам просто необходимо проинициализировать мир заново и начать геймплей, при смерти игрока — закинуть его обратно в состояние меню. Стейты представляют из себя три указателя на функции: переход (инициализация), обновление и отрисовка.
typedefvoid(CGameStateCallback)();
Поскольку мы хотим некоторой гибкости при создании новых классов противников, то вводим структуру CEnemyClass, которая описывает визуальную составляющую врагов и их флаги — могут ли они стрелять по игроку или просто летят вниз (астероиды), как они передвигаются (зигзагами например) и т. п.
Всё! Для текущего уровня реализации игры этого достаточно :) Переходим к реализации игровой логики. Вообще, динамический аллокатор в играх для китайских платформ лучше использовать как можно меньше. Heap'а довольно мало (~600Кб), да и не совсем понятно, как этот аллокатор реализован, есть вероятность, что используется аллокатор и куча основной ОС.
Начинаем с реализации полёта кораблика. Для этого он должен реагировать на стрелки и не улетать за границы экрана, а ещё для красоты он должен «вылетать» из нижней границы экрана при старте игры:
Переходим к динамическим пулам с объектами. Как вы уже заметили, их всего два — враги и летящие снаряды. Реализация спавна врагов/снарядов простая и понятная: мы обходим каждый элемент пула, если указатель на объект не-нулевой, значит объект всё ещё жив и используется на сцене. Если нулевой — значит ячейка свободна и можно заспавнить новый объект:
При обходе пула во время обновления кадра, мы обновляем состояние каждого объекта и если его функция Think вернула true, значит объект больше не нужен и его нужно удалить:
if (enemyThink(world.enemyPool[i]))
{
sysFree(world.enemyPool[i]);
world.enemyPool[i] = 0;
}
А вот и реализация Think:
boolenemyThink(CEnemy* enemy) {
enemy->y += enemy->_class->speed;
if (enemy->y > gGetScreenHeight() || enemy->health <= 0) return true;
return false;
}
Но кораблики должны же откуда-то появляться! Для этого у нас есть переменная nextSpawn, которая позволяет реализовать самый простой тип спавнера — относительно времени (или в нашем случае тиков):
world.nextSpawn--;
if (world.nextSpawn < 0) {
CEnemy* enemy = spawnEnemy(&enemyClasses[0]);
world.nextSpawn = randRange(40, 70);
}
Результат: мы уже можем полетать, пострелять и поуворачиваться от вражеских корабликов!
Уже что-то напоминающее игру! Осталось лишь добавить подсчет очков, менюшку, разные виды противников, возможно какие-то бонусы и у нас будет готовая простенькая аркада. В целом, выше приведена достаточно неплохая архитектура для простых 2D-игр на Plain C. Фактически, она может быть хорошей базой и для ваших игр: в теме о китах на 4pda я встречал немало людей, которые банально не знали, с чего начать.
❯ Что у нас получилось?
Но без тестов на реальных устройствах материал не был бы таким интересным! Поэтому давайте протестируем игру на двух реальных телефонах, как вы уже догадались, один — Nokla TV E71, а второй — клон Nokia 6700, который подарил мне мой читатель Никита.
На TV E71 игра идёт не сказать что очень бодро. Кадров 15 точно есть, что, учитывая разрешение 240x320, весьма неплохо для такого девайса.
а 6700,, даже учитывая более низкое разрешение — 176x220, дела примерно также — ~15FPS! Но поиграть всё равно можно. Уже хотите написать «автор наговнокодил, а теперь ноет из-за низкого FPS»? Ан-нет, я попробовал игры сторонних разработчиков — они идут примерно также :( К сожалению, таковы аппаратные ограничения устройства.
Исходный код игры с Makefile'ами и файлами проектов для Visual Studio и MRELauncher доступны на моём GitHub. Свободно изучайте и используйте его в любых целях :)
❯ Заключение
Но в остальном же, демка получилась довольно прикольной, как и сам опыт программирования для китайских телефонов. В общем и целом, китайцы пытались максимально упростить API и привлечь разработчиков к своей платформе. Если ради примера взглянуть на API для Elf'ов на Motorola, можно ужаснуться от state-based архитектуры платформы P2K. А тут тебе init, event, draw — и всё!
Но популярности помешала непонятная закрытость платформы, костыльный запуск программ, отсутствие нормального симулятора. А ведь сколько фишек было: даже возможность писать и читать память ядра! А вы как считаете? Можно ли вдохнуть в китайские кнопочники новую жизнь, узнав о наличии возможности запуска нативного кода на них?
P. S.: Друзья! Время от времени я пишу пост о поиске различных китайских девайсов (подделок, реплик, закосов на айфоны, самсунги, сони, HTC и т. п.) для будущих статей. Однако очень часто читатели пишут «где ж ты был месяц назад, мешок таких выбросил!», поэтому я решил в заключение каждой статьи вставлять объявление о поиске девайсов для контента. Есть желание что-то выкинуть или отправить в чермет? Даже нерабочую «невключайку» или полурабочую? А может, у этих девайсов есть шанс на более интересное существование! Смотрите в соответствующем посте, что я делаю с китайскими подделками на айфоны, самсунги, макбуки и айпады! Да и чего уж там говорить: эта статья уже сама по себе весьма наглядный пример! Найти меня можно в комментариях тут, на Пикабу, и в тг @monobogdan
Понравился материал? У меня есть канал в Телеге, куда я публикую бэкстейдж со статей, всякие мысли и советы касательно ремонта и программирования под различные девайсы, а также вовремя публикую ссылки на свои новые статьи. 1-2 поста в день, никакого мусора!
Полезный материал?
Были ли у вас такие китайчики?
Материал подготовлен при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud, дабы не пропускать новые статьи каждую неделю!