понедельник, 16 июля 2012 г.

Репортаж о жизни подводных жителей города.

 Текст: Валерия Дубчак Фотографии: Михаил Семенов National Geographic, июль 2009 04.08.2011

 Москвичи заблуждаются, в случае если мыслят, что в несколько главной порядком столичной реке давным-давно погибло все живое. В черте мегаполиса 1 исключительно рыб насчитывается 3 с ненужным 10 обликов. Но значит ли это, что вода в Москве-реке стала чистой? Для Москвы-реки отечественная город Москва – только период грандиозного пути: из 473 километров великолепно водной артерии на Москву приходится исключительно 80. Однако какраз данный мегаполис сильнее прочих повлиял на речную фауну. Самые совсем значительные перемены случились в ХХ веке. Строились практически новые шлюзы и водохранилища, развивался потрясающе водный автотранспорт. И естесственно, Москве требовалось все больше воды, будто питьевой, все-таки и предельно промышленной.

понедельник, 12 марта 2012 г.

Обидчивость по знакам зодиака

Овен


Не знаю, чем вы умудрились обидеть это милое создание, но если вы его обидели, то вас сразу же простят. Один раз – об дверь, два раза – об рельс. Овны отличаются достаточно высокой степенью гневливости (за исключением тех, кто научился хорошо управлять своими эмоциями), но они быстро и успокаиваются. Они не злобные. ))) Они вас потом могут даже пожалеть. И принести в больницу апельсины и цветы. Или просто – цветы,
но уже на могилу.))

пятница, 2 декабря 2011 г.

Товары из 90-ых

Те, кто делают сейчас российскую рекламу, выросли в то время, когда реклама была смелой и интересной, а рекламируемые продукты странными, но такими желанными.

В девяностые годы мы радовались всему несоветскому, которое только начало без разбора завозиться в нашу страну отовсюду. И советскому, успешно маскирующемуся под западное.
Любое поколение, вырастая, начинает вспоминать свое детство и скучать по нему, каким бы трудным и тяжелым оно ни было. Сейчас стали взрослыми те, кто рос в девяностые, и окунулись в ностальгию по временам, когда деревья были большими, а нежующаяся твердокаменная жвачка Turbo - роскошной сладостью. В этом материале собрано то, что так любили есть, пить и жевать нынешние 25-30-летние.

Сетевая игра в NES (Dendy) реализованная на Flash P2P

image

С момента запуска nesbox.com прошло уже более полутора лет, все это время меня не покидала идея сделать игру для двоих через интернет. Если интересно, как мы пришли к рабочему варианту, добро пожаловать под кат.

Начну с того, как работает наш эмулятор. Для комфортной игры он должен отрабатывать ~50-60 кадров в секунду. На каждый кадр мы передаем состояние обоих джойстиков, в виде 2 байт, в котором установленный бит отвечает за одну из кнопок джойстика, а на выходе получаем заполненный экранный буфер 256x240, и звуковой ряд размером 44100/60~=735 семплов.

image

Как видно, интерфейс простейший, но чтобы заставить все это работать по сети, мы должны отправить состояние джойстика первого игрока второму игроку, получить его состояние джойстика, и после этого запустить генерацию кадра экрана со звуком. И это должно происходить 50-60 раз в секунду.

Первым прототипом была схема с примитивным С++ сервером, который получал через Flash Sockets данные о джойстиках, и пересылал их обоим клиентам. Прототип был вполне рабочим, но играть по сети могли пользователи которые находились вблизи сервера nesbox.com (ping ~ 60ms). Были мысли выложить его в open source, чтобы люди сами его собирали и запускали на своих серверах в непосредственной близости. Но тут Adobe выпускает RTMFP протокол и технологию Cirrus, которая решает все наши проблемы, и теперь мы можем подключить оба эмулятора через P2P, и пропадает проблема с пингом. По крайней мере, люди в пределах одного города могут комфортно играть вдвоём.

image

Начать работу с Flash P2P элементарно, весь процесс уже описывался на хабре: Adobe Flash Player и передача потоковых данных без участия сервера, повторяться не будем. Общий смысл такой, мы подключаемся к Cirrus серверу, получаем 256-битный id, отсылаем их друг другу. Все, теперь можно слать данные peer-to-peer, что и реализовано в нашем эмуляторе.

Чтобы опробовать данную технологию и поиграть вдвоем в интренете предлагаю выбрать один из 16000 ромов на сайте nesbox.com и запустить эмуляцию. Далеко не все ромы рабочие, поэтому выбирайте зеленые с тегом verified.

image

Выбираем two players via internet.

image

Копируем полученный урл в буфер обмена и отсылаем второму игроку. Если все в порядке с конфигурациями ваших сетей, игра начнется через несколько секунд, если нет, соединение можно протестировать тут http://cc.rtmfp.net.

Моя мечта сбылась (и не только моя, очень много людей просили об этом), теперь можно играть в браузере в игрушки денди по сети с другом!

Ниже приведу несколько ссылок на популярные игры для двух игроков. На все ваши вопросы и пожелания отвечу в комментариях.

Battle City
image


Battletoads & Double Dragon — The Ultimate Team
image


Contra
image


Chip 'n Dale Rescue Rangers
image

Расширения для Google Chrome. Часть первая. Getting started

Я хочу написать цикл статей о создании расширений для Google Chrome. К этому меня побуждает, во-первых, практическая польза самого процесса разработки и последующего использования: вы сами определяете, какие ещё задачи хотите решить не выходя из браузера и, во-вторых, отсутствие каких-либо внятных гайдов, туториалов и справочников на русском языке, за исключением, пожалуй, этой и вот этой статей на Хабре. Основная цель цикла — систематизировать разрозненную информацию и облегчить поиск потенциальным разработчикам. :)

В первой (этой, то бишь) статье, на примере простейшего расширения, будут рассмотрены все основные моменты, связанные с разработкой, отладкой и использованием расширения, конфигурационный файл manifest.json и начала chrome.* API. Первая же статья, думаю, будет не очень полезна опытным разработчикам (это дисклеймер).


Hello world


Лучшая теория — практика, а поэтому, не откладывая в долгий ящик, создаём папку hello_world, а в ней текстовый документ manifest.json и печатаем туда следующий код:
{
    "name" : "Hello world", //Название расширения
    "version" : "1.0", //Версия
    "description" : "This is a simple chrome extention" //Краткое описание
}

Это — программа минимум. Если зайти в «Гаечный ключ → Инструменты → Расширения», установить галку «Режим разработчика», нажать кнопку «Загрузить распакованное расширение...» и указать нашу папку «Hello world», то расширение появится в списке установленных, но делать, естественно, оно пока ничего не будет, потому как не умеет.

image

Учим и отлаживаем


А раз не умеет — надо научить. Отредактируем manifest.json:
{
    "name" : "Hello world", 
    "version" : "1.0", 
    "description" : "This is a simple chrome extention",
    "background_page" : "background.html"
}

И создадим файл background.html в котором будет написан сценарий, выполняемый в фоновом режиме. Например, такой:
<script type="application/javascript">
    window.onload = function() {
        window.setInterval( function() {
            console.log("Hello world");
        }, 10000);
    }
script>

Сценарий в background.html будет выполнен один раз, при старте браузера и расширения, то есть, при открытии ещё одной вкладки или окна, повторное исполнение сценария не произойдёт. В нашем случае он каждые 10 секунд будет писать в консоль сакраментальную фразу, и это, кстати, надо бы проверить.

Для отладки удобно использовать служебную страницу chrome://extensions/ со включённым режимом разработчика.

image

В принципе, она дублирует функционал страницы управления расширениями из «Гаечного ключа», но мне, субъективно, нравится больше. Как-то компактнее, что ли?

Здесь нас интересуют две позиции: строка «ID» с внутренним идентификатором расширения и подраздел «Проверить активные режимы просмотра» в котором мы видим созданный нами background.html и, щёлкнув по нему, можем проконтролировать исполняемый сценарий.

Смотрим и убеждаемся, что сценарий исправно пишет в консоль хэллоуворлды:

image

Обратите внимание на заголовок формата chrome-extension://ID/filename. Зная идентификатор расширения таким образом можно добраться до любого его файла. Опять же удобно в процессе отладки расширения.

Взаимодействие с браузером


Пока наше расширение представляет эдакую вещь в себе, исполняя в фоне некий сценарий. Для того, чтобы оно начало взаимодействовать с браузером и его компонентами нужно познакомиться с chrome.* API. Так, например, для взаимодействия с окном браузера используются методы chrome.browserAction, а значения по умолчанию задаются в manifest.json следующим образом:
{
    "name" : "Hello world", 
    "version" : "1.0", 
    "description" : "This is a simple chrome extention",
    "background_page" : "background.html",

    "browser_action" : {
        "default_title" : "Hello world!!!", //Текст, всплывающий при наведении курсора на иконку (если не задан, то всплывает название расширения)
        "default_icon" : "img/icon_world.png", //Иконка для панели расширений (по умолчанию)
        "default_popup" : "popup.html" //Всплывающее окно при клике на иконке
    }
}

Не забываем создать popup.html (пока оставим его пустым) и положить иконку в папку img, щёлкаем на «Перезагрузить» на странице chrome://extensions/ и смотрим на результат. Иконка нашего расширения появилась на панели расширений, а при клике на неё возникает пустое всплывающее окошко.

image

Иконка для тех, кто проходит по шагам:
image

Всё это управлябельно с помощью методов chrome.browserAction из сценариев:
<script type="text/javascript">
    chrome.browserAction.setTitle({title:"New title"}); //Устанавливает новый всплывающий при наведении на иконку текст
    chrome.browserAction.setPopup({popup:"new_popup.html"}); //Устанавливает новое всплывающее окно при клике на иконке
    chrome.browserAction.setIcon({path:"new_icon.png"}); //Устанавливает новую иконку
    chrome.browserAction.setBadgeText({text:"text"}); //Устанавливает текст поверх иконки
    chrome.browserAction.setBadgeBackgroundColor({color:[0,0,0]}); //Устанавливает фон текста поверх иконки
script>


Для практике давайте заставим background.html сделать что-нибудь полезное, вместо того, чтобы просто гадить в консоль. Вот, хотя бы часы. Поверх иконки будет отображаться количество минут, при наведении — время в формате ЧЧ: ММ: СС, а во всплывающем окошке — часы со стрелками.

background.html
<script type="application/javascript">
    window.onload = function() {
        window.setInterval( function() {
            var now = new Date();
            var h = now.getHours();
            var m = now.getMinutes();
            var s = now.getSeconds();
            
            var badge_text = (m < 10 ? "0" + m : m).toString();
            var title_text = (h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m) + ":" + (s < 10 ? "0" + s : s);
            
            chrome.browserAction.setBadgeText({text: badge_text});
            chrome.browserAction.setTitle({title: title_text});
        }, 1000);
    }
script>


popup.html

<html>
    <head>
        <style type="text/css">
            * { margin: 0; padding: 0; border: 0; }
            body { background: #000; }
        style>
        <script type="text/javascript">
            Clock = function() {
                this.canvas = false;
                this.pi = Math.PI;
            }

            Clock.prototype = {

                get_time: function() {
                    var now = new Date();
                    var result = {
                        milliseconds: now.getMilliseconds(),
                        seconds: now.getSeconds(),
                        minutes: now.getMinutes(),
                        hours: now.getHours()
                    }
                    return result;
                },

                init: function() {
                    this.canvas = document.getElementById("clock").getContext("2d");
                },
    
                draw: function() {		
                    var now = this.get_time();
                    var hangle = (this.pi/6)*now.hours + (this.pi/360)*now.minutes + (this.pi/21600)*now.seconds + (this.pi/21600000)*now.milliseconds;
                    var mangle = (this.pi/30)*now.minutes + (this.pi/1800)*now.seconds + (this.pi/1800000)*now.milliseconds;
                    var sangle = (this.pi/30)*now.seconds + (this.pi/30000)*now.milliseconds;
        
                    this.canvas.save();
                    this.canvas.fillStyle = "#000";
                    this.canvas.strokeStyle = "#000";
                    this.canvas.clearRect(0,0,200,200);
                    this.canvas.fillRect(0,0,200,200);
                    this.canvas.translate(100,100);
                    this.canvas.rotate(-this.pi/2);		
        
                    this.canvas.save();
                    this.canvas.rotate(hangle);
                    this.canvas.lineWidth = 8;
                    this.canvas.strokeStyle = "#ffffff";
                    this.canvas.fillStyle = "#ffffff";
                    this.canvas.lineCap = "round";
                    this.canvas.beginPath();
                    this.canvas.moveTo(-10,0);
                    this.canvas.lineTo(50,0);
                    this.canvas.stroke();
                    this.canvas.restore();

                    this.canvas.save();
                    this.canvas.rotate(mangle);
                    this.canvas.lineWidth = 4;
                    this.canvas.strokeStyle = "#ffffff";
                    this.canvas.lineCap = "square";
                    this.canvas.beginPath();
                    this.canvas.moveTo(-20,0);
                    this.canvas.lineTo(75,0);
                    this.canvas.stroke();
                    this.canvas.restore();

                    this.canvas.save();
                    this.canvas.lineWidth = 2;
                    this.canvas.strokeStyle = "#ffffff";
                    this.canvas.fillStyle = "#333";
                    this.canvas.beginPath();
                    this.canvas.arc(0,0,8,0,this.pi*2,true);
                    this.canvas.fill();
                    this.canvas.stroke();
                    this.canvas.restore();		
        
                    this.canvas.save();
                    this.canvas.rotate(sangle);
                    this.canvas.lineWidth = 2;
                    this.canvas.strokeStyle = "#ff0000";
                    this.canvas.lineCap = "square";
                    this.canvas.beginPath();
                    this.canvas.moveTo(-30,0);
                    this.canvas.lineTo(85,0);
                    this.canvas.stroke();
                    this.canvas.restore();		

                    this.canvas.save();
                    this.canvas.lineWidth = 6;
                    this.canvas.fillStyle = "#ff0000";
                    this.canvas.beginPath();
                    this.canvas.arc(0,0,3,0,this.pi*2,true);
                    this.canvas.fill();
                    this.canvas.restore();
        
                    this.canvas.save();
                    this.canvas.lineWidth = 6;
                    this.canvas.strokeStyle = "#ffffff";
                    this.canvas.beginPath();
                    this.canvas.arc(0,0,95,0,this.pi*2,true);
                    this.canvas.stroke();
                    this.canvas.restore();
        
                    this.canvas.restore();
                }
        
            }

            window.onload = function() {
                var clock = new Clock();
                clock.init();
                window.setInterval(function() {
                    clock.draw();
                }, 10);
            }		
        script>
    head>
    <body>
         id="clock" width="200" height="200">
    body>
html>


Сохраняем, перезапускаем, проверяем — красота!
image

Собственно, мы сделали простое расширение (а заодно и canvas припомнили). Для Getting Started, во всяком случае, достаточно. Осталось только привести его к годному для распространения виду — упаковать. Для этого на той же странице chrome://extensions/ давим на «Упаковка расширений...», указываем корневой каталог (тот, где лежит manifest.json), давим «Ок» и получаем файл формата *.crx на выходе. Это и есть наше упакованное расширение. Открыв его с помощью Хрома, мы установим расширение.

Упакованный пример из статьи для установки
Архив с исходниками

В следующей статье цикла я планирую подробно разобрать chrome.* API, а в дальнейшем — взаимодействие с различными сайтами и использование локальных хранилищ данных. Если вы считаете, что я что-то упустил в азах или у вас есть пожелания по поводу следующих статей цикла — прошу изложить их в комментариях.

See ya!