Джаваскрипт гайд

Автор Proydoha, 26-09-2012, 00:09:03

« предыдущая - следующая »

0 Пользователей и 1 гость просматривают эту тему.

Proydoha

26-09-2012, 00:09:03 Последнее редактирование: 26-09-2012, 08:21:29 от Catcatcat
Короче я спросил двух человек и уже, как минимум, двое согласились принять участие. Замес такой: если я напишу руководство про написание игр в формате "для даунов и дегенератов" - согласившиеся (и остальные заинтересованные) пробуют себя в этом деле.

Специально для этой цели я собираюсь использовать в качестве примера язык javascript, потому что он тоже сделан для именно такой аудитории:"Не поставил точку с запятой в конце строки? Не вопрос, дружище. Все мы люди и понимаем, что точка с запятой там обязана быть. Не инициализировал переменную? Не вопрос. Я инициализирую её за тебя. Это приведёт к непредсказуемым и увлекательным резульатам, но ведь это лучше, чем ничего?"

Кроме того джаваскрипт имеет ряд невообразимо прекрасных положительных моментов: не надо компилятор - всё выполняется сходу в броузере; не надо кидать исполняемый файл друганам, что б они заценили высоту твоего мастерства - достаточно кинуть ссылку и события развернуться прямо в окне их броузера. Для особо рукастых в особо новые броузеры встроена возможность задействовать силы видеокарты, что бы выдавать особенно сочную трёхмерную картинку. Речь об интернет эксплорере, конечно, не идёт.

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

Это быдлокодерский гайд, я пишу его в режиме "поток сознания". Что бы уметь делать правильно надо читать учебники/справочники, а не его.
Хороший справочник: http://www.w3schools.com/js/

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


Закончили со вступительным словом.

Щас, во всех учебниках по программированию, должна идти глава, в которой рассказывается про то, что такое переменные и про то какой синтаксис у языка. При любых других обстоятельствах я б тактично умолчал о своих нулевых познаниях в области рассказывания про это. Нет, првада. Как рассказать, что такое (например) переменная, предполагая, что собеседник вообще не в теме?

Ладно, так как я тоже попадаю в категорию дебилов, то я расскажу как бы до всего доходил я.

Перво-наперво надо создать текстовый документ. Я уже спецом отдельно создал папку lessons_for_retards. В ней создаю файл lesson_1.php. Мне выгодно создавать файлы с расширением .php, потому что оно у меня в системе проассоциировано с блокнотом, в отличие от .html, который ассоциируются с броузером.

Вот его содержимое:

lesson_1.php

<!DOCTYPE HTML>

<html>

<head>
<script type="text/javascript" src="lesson_1.js"></script>
</head>

<body style="background-color:#FFFFFF">

  <div style="width: 1024px; margin: auto auto auto auto;" >
   <canvas id="game" width="1024" height="768" style="border: 1px solid black">
    Canvas is not supported.
   </canvas>
  </div>

</body>

</html>


САМОЕ ГЛАВНОЕ НА ЭТОМ ЭТАПЕ(!!!): НЕ НАДО ДУМАТЬ О СОДЕРЖИМОМ ЭТОГО ФАЙЛА(!!!) Будем считать его "служебным". Написали один раз и забыли. В нём описана уэб-страница на которой находится наше поле для рисования игрулек: ему установлены размеры 1024 на 768 пикселя, оно обведено красивой чёрной линией и установлено в самый центр белого экрана.

Почему надо не думать о содержимом этого файла? Потому что весь джаваскрипт лучше вынести в отдельный файл
(строчка <script type="text/javascript" src="lesson_1.js"></script> сообщает, что этот файл называется lesson_1.js
Надо его немедленно создать и положить рядом с файлом lesson_1.php!)

Ну, то есть, мы тут не интернет-страницы верстать учимся, а игрульки писать и лишний "мусор" будет только мешать.
Закрвыаем lesson_1.php в блокноте, открываем lesson_1.js в блокноте.
Открываем lesson_1.php в броузере. Должен появиться брутальный чёрный прямоугольник на брутальном белом фоне.

А теперь, внимание, открываю самый главный секрет программирования: программы выполняются слева-направо, сверху вниз.

После раскрытия самого главного секрета программирования рассказываю про самую главную команду в javascript. Для нагладности предлагаю сразу записать её в файл lesson_1.js. Вот она:

alert("Лол!");

Затем сохранить его и нажать в броузере f5 при открытой в нём странице lesson_1.php. Если до сих пор не все догадались о том как открыть файл своим броузером подсказываю самый лёгкий вариант: перетащить его из папочки в окошко броузера.

Если всё было сделано правильно, то перво-наперво поверх страницы вылезет окошко с надписью "Лол!" и кнопкой "ОК". Это может показаться не очень возбуждающим для начала, но в этом скрыт невероятный потенциал. Можно, например, написать несколько команд alert в ряд и получить целый набор сообщений!

alert("Лол!");
alert("Это");
alert("не");
alert("очень");
alert("возбуждающе");
alert(":(");


Экспериментально убедившись в том, что команда alert показывает текст, написанный в скобочках, перейдём к осознанию того, что такое переменные.

Строго говоря тут всё просто, как на уроке математики в школе.

Например, вместо множественных алертов пишем:

a=5;
b=3;
c=a b;
alert(c);


Обновляем страницу, видим окошко, в котором написано 8.
Сказано вывести содержимое, записанное под буквой c - так всё и произошло.

Определимся с терминологией: значёк "=" (без кавычек) обозначает присваивание. Буковке слева то него (это может быть и слово, и слово с циферками, и несколько слов, соединённых нижними подчёркиваниями - лишь бы пробелов между ними не было) присваивается число, стоящее справа. Эта буква/слово/что угодно и называется переменной.

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

Если написать вот так, то точки с запятой строго обязательны:

a = 5; b = 3; c = a   b;
alert(c);


Вообще-то переменные надо инициализировать. Это такая операция, при которой для переменной отводится кусочек компьютерных мозгов, которые будут помнить её содержмое. Но джаваскрипт язык для идиотов и если просто сходу присвоить неинициализированной переменной какое-то значение она инициализируется сама собой. Это ерунда, у нас тут у всех тысячеядерные компы и думать о инициализации не надо. Но если очень хочется, то делается это вот как:

var a,b,c;               //Это я инициализировал переменные a,b и c
a = 5; b = 3; c = a - b; //Это я присвоил им значения.
alert(c);                //Это я вывел результат во всплывающем окошке


Получилось 2.

Всё, что написано после // и до конца строки называется комментарием и компьютер его не видит. То есть после // можно писать матерные слова и о них никто не узнает. Многострочные комментарии надо засовывать в /* вот такие */ кавычки(?)

Ещё один важный момент, связанный с переменными: там, выше, явно видно, что слова я брал в кавычки, что бы их вывести в окошке alert-а. Здесь же я кавычек не пишу. Почему так?

Если б речь шла о каком-то другом языке программирования, то я бы сказал, что я всегда думал, что если буду  рассказывать о переменных в школе, буду объяснять это так: вот, представьте игрушку для совсем маленьких детей. Такая стеночка или коробочка и в ней прорезаны отверстия разных форм, а в придачу к ней идут фигурки - звёзочки, пирамидки, цилиндрики, параллелепипедики. Задача карапуза - всовывать правильные фигуры в правильные отверстия - потому что в неправильные они тупо не пролазиют.

Это объяснение имело бы смысл, но в джаваскрипте всё гораздо проще (для дебилов же). Есть, грубо говоря, три типа переменных - три дырки в коробке: это числа, буквы и результаты логических действий. Что бы в переменную записать строку надо взять эту строку в кавычки. Что бы записать число - надо просто написать число.

Стираем всё, что понаписывали в lesson_1.js. Я даже, думаю, зря я назвал файл lesson_1, всё равно для удобства мы работаем с одним файлом, а не плодим на каждый чих новую копию.

Пишем:

var playerX = 5;           //Инициализировал переменную и присвоил ей числовое значеие.
var playerY = 30;          //Инициализировал переменную и присвоил ей числовое значеие.
var playerName = "RETARD"; //Инициализировал переменную и присвоил ей строковое значение.
alert("Name: "   playerName   "\n"   "X: "   playerX   "\n"   "Y: "   playerY); //Магия


Если обновить страницу получится такой результат:

Name: RETARD
X: 5
Y: 30

Это мы представили себя в середине какого-то дебага нашей игры, в котором на экран вылазиет критически важная информация о игроке: его имя и его координаты. В отличие от языков для умных людей джаваскрипт сам преобразовывает численные переменные в строковые. Не наоборот. Для наоборот надо использовать функции языка.

О чём я говорю? Вот о чём:

var a = 5;
var b = 5;
var c = a   b;
alert("a   b = " c);

a = "5";   //Переменные я инициализировал раньше. Писать var теперь не нужно.
b = "5";
c = a   b;
alert("a   b = " c);


Результаты is pretty self-explanatory. Забыл русский язык. Как "самообЪясняюий" будет по русски? "Очевидные"? Гугл говорит, что "Не требующие объяснения". Результаты объяснения не требуют.

Оукей. Мы уже знаем почти всё, что необходимо для создания игр.

Щас произойдёт сразу много магии, но, честное слово, я больше всего ненавижу моменты в обучающих книжках, где написано:"Оу. Ты научился создавать переменные и присваивать им значения. Поупражняйся с этой скучнейшей задачей самостоятельно, а затем мы потратим ещё полчаса, что бы понять как работает примитивное if"

Примитивное if, кстати, работает так: сперва пишется слово if, затем, обязательно в скобках, пишется (условие), затем одно действие, которое выполняется при этом условии.
Примеры легче, чем слова!

if (условие) действие;

var a = 1;
if (a == 1) alert("Действие");


Действием в данном случае является вывод окошка со словом "Действие", а условием равность переменной a еденице.
Это знание пришло ко мне не сразу, но на самом деле результатом логической операции является переменная логического типа. Они могут принимать только одно из двух значений: true (истина) или false(ложь). Их можно присваивать либо напрямую:

var a = true;
alert(a);


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

var a = 5;
var b = 10;
var c = (a > b);
alert("a больше b? Правильный ответ: " c);


Так вот. Если в результате в условии у if получится true, то действие записанное за ним выполнится. Если нет - оно будет проигнорировано.

Самые нужные логические операторы:
== - равно
!= - не равно
>  - больше
<  - меньше
>= - больше или равно
<= - меньше или равно

Все остальные крутые операторы простым смертным не нужны, хоть они и существуют.

Если за один раз надо проверить сразу несколько условий надо прибегнуть к помощи следующих штук:

|| - или
&& - и
!  - не

Например:

var a = 5;
var b = 10;
var c = 15;
if ((a == 5) && !(c == 30)) alert("Это условие примет значение true, \nтак как a действительно равно пяти,\n а c НЕ равно традцати!");


Ну вот, опять отвлеклись. Я хотел сделать много магии сразу и походу дела объяснять почему именно так, а не иначе.

"Обычно" я делаю шаблон, в который я дописываю всякие штуки и получается что-то похожее на игру. Щас я напишу большой кусок такого шаблона и попытаюсь донести идею того как он работает.

Стираем всё, если это ещё не было сделано и записываем в файл lesson_1.js следующее:

var canvas;
var context;

function update()
{

}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 100);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Обожемой, штоэта? Ни слова про всю эту ерунду раньше сказано не было! Окей, про всю, кроме if!

Этот код делает следующее:


var canvas;  //инициализирует переменную, в которой будет храниться "холст" (серъезно, так и переводится) для                      //рисования на нём
var context; //инициализирует переменную, в которой будет храниться контекст. Я не могу объяснить словами что это,
             //но, короче говоря, с помощью него осуществляется процесс рисования.

function update() //Это описание функции update(). Пустые скобки означают, что в эту функцию не передаётся никаких
{                 //параметров, как, например, в alert передаётся строка, которую она выводит во всплывающем окне.
                  //Фигурные скобки означают где начинается список действий, которые она делает, и где он
}                 //заканчивается. Сейчас эта функция не делает ничего, но она является сердцем будущей игры.

window.onload = function() //здесь событию, которое срабатывает как только загрузится страница, описанная в
                           //lesson_1.php, присваивается функция. Короче говоря как только страница загрузится до
                           //конца выполнятся следующие действия:

{ //Это начало списка этих действий. Вообще говоря это называется 'операторские скобки'. Если необходимо описать
  //функцию используются они. Если необходимо, что бы после if выполнилось больше одной команды
  //используются снова они. Короче, если надо объеденить группу действий в одно - их надо взять в такие скобки.

canvas  = document.getElementById("game"); //С переменной canvas ассоциируется элемент canvas, находящийся на
                                            //странице. Если открыть lesson_1.php, то можно увидеть, что я дал
                                            //ему имя game.

if (canvas.getContext) //Это проверка возможности получения контекста холста. На самом деле её можно не делать
                        //так как во всех современных броузерах он поддерживается. Ну, кроме, может некоторых
                        //версий IE.

{ //Это то, о чём я говорил. Нам надо сделать более чем одно действие при условии, что мы можем получить контекст
   //Открываем для этого фигурную скобку, что бы объеденить эти действия воедино.

  context = canvas.getContext("2d"); //Получаем "контекст". Не надо думать что это значит.
 
  setInterval(update, 100); //Функция setInterval вызывает другую функцию (первый параметр в её скобках) каждый
                            //определённый в милисекундах интервал времени (второй параметр, записанный через
                            //запятую. Все параметры функций разделяются запятыми). Таким образом мы
                            //приказываем нашей программе каждые 100 милисекунд (каждую 0.1 секунду) вызывать
                            //функцию update(). Если вдруг память подводит: функция update описана выше и она (пока)
                            //ничего не делает.

}    //Скобка закрывается

else //Слово else пишется в if, если надо описать какие действия надо выполнять, если его условие
      //приняло оказалось ложным. В этом случае: если контекст не может быть получен. Можно открыть страницу старым
      //броузером и убедиться, что оно сработает (или можно провести эксперимент самостоятельно на собственном              //условии, как это людят просить делать в учебниких).

{ //Это для параноиков. Хоть действие и одно, но я всё равно заключу его в скобки.

   alert("Не получилось получить контекст!"); //Если вдруг на страницу зашел динозавр следует его разочаровать.

} //Скобка закрывается
}  //Закрывается скобка, символизирующая конец функции.



По поводу всех этих скобок. Всё прогрессивное человечество, выросшее на сиподобных языках, предпочитает использовать так называемые "егоипетские скобки". Почему египетские?

Вот почему:


Если кому-то интересно моё мнение - вот оно: это идиотизм.

Я пишу закрывающую скобку СТРОГО ПОД открывающей. Моя психика была деформирована Паскалем в школе, да. Чётко вижу какие закрывающие скобки соответствуют каким открывающим. Изменить моё мировоззрение меня заставят только пытки.

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

В случае лажи могут случиться следующие штуки:
1.Ничего видимого не произойдёт.
2.Что-то, что должно было случиться, не случится.
3.Счётчик кадров покажет ноль.
4.Страница загрузится, но на холсте ничего не будет нарисовано вообще.

Новая версия оперы опровергла моё учение о счётчике fps

Последние два наиболее вероятны. Как найти где ты облажался?

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

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

Итак. Счётчик FPS.

Ладно. Так уж и быть. Ещё одно лирическое отступление. Не могу себя сдерживать. Игры состоят из двигающихся картинок. Что бы картинка двигалась надо каждый следующий кадр рисовать заново и стирать старый. Нарисовали чёрный квадрат на белом фоне, зарисовали всё белым, нарисовали новый чёрный квадрат на полногтя правее старого,.. Повторяем эту последовательность всё время и чёрный квадрат вроде как плывёт вправо.

Рисовать квадраты - самое милое дело. Перво-наперво запишем следующие две строчки между фигурных скобок функции update:

context.fillStyle="#FFFFFF";
context.fillRect(0,0,canvas.width,canvas.height);


В итоге должно получиться следующее. Всё равно никто никогда при чтении учебников никогда не набирает сам примеры:

var canvas;
var context;

function update()
{
context.fillStyle="#FFFFFF";                      //Эту строчку мы дописали
context.fillRect(0,0,canvas.width,canvas.height); //Эту строчку мы дописали
}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 100);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Если щас обновить страницу, то вроде бы ничего визуально не изменилось, но на самом деле это не так.

context.fillStyle="#FFFFFF"; //Это мы присваиваем "контексту" белый цвет "заливки". Суть вот в чём:
                             //Геометрические фигуры при рисовании можно либо обвести по периметру (это называется
                             //stroke), либо "залить" (это называется fill). Для того, что бы писать буквы поверх
                             //экрана тоже используется цвет "заливки".
                             //Цвет белый потому что #FFFFFF - это в шестнадцатиричном коде так зашифрован белый цвет
                             //Можно загуглить "коды цветов" и брать эти числа оттуда, а можно разобраться самому
                             //как это работает и вписывать буквы и цифры по своему хотению.

context.fillRect(0,0,canvas.width,canvas.height); //Это функция рисования прямоугольника. Всё происходит с помощью
                                                  //чёртового контекста, поэтому надо не забывать дописывать его
                                                  //перед каждым волшебно-рисовательным действием.
                                                  //Здесь мы командуем "залить" прямоугольник белого цвета.
                                                  //Заливать мы приказываем с верхнего левого угла экрана
                                                  //(первые два нуля в скобках намекают на это, они символизируют
                                                  //координаты х и y, только отсчёт ведётся с верхнего, а не с
                                                  //нижнего левого угла экрана) и ширина с высотой
                                                  //этого прямоугольника должна быть равна ширине и высоте экрана.


Строго говоря вторую строчку можно было бы записать так:

context.fillRect(0,0,1024,768);

И даже следовало бы остановиться на этом объяснении, так как про объекты я не рассказал, и про то, что это свойства объекта "контекст" мы используем для рисования, и что ширина и высота холста это части объекта "холст", записанные через точку.

Для наглядности нарисуем ещё один прямоугольник меньших размеров чёрного цвета:

var canvas;
var context;

function update()
{
context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";   //  <<--------------- всё внимание сюда
context.fillRect(50,30,10,20); //  <<--------------- всё внимание сюда
}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 100);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Перво-наперво мы сказали, что цвет заливки, после того как мы закрасили всё белым, теперь чёрный. А потом приказали нарисовать прямоугольник координаты верхнего левого уголка которого: 50 пикселей от левой границы, 30 пикселей от верхней границы; а ширина и высота соответсвенно 10 и 20 пикселей.

Обновляем экран. Видим плоды усилий.

А, и про движение на полногтя:

var canvas;
var context;

var x = 50; //  <<------------------------------------------------ всё внимание сюда

function update()
{
context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,30,10,20); //  <<----------------------------- всё внимание сюда
x  ; //  <<------------------------------------------------------ всё внимание сюда
}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 100);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Обновляем страницу - ползёт. Если, кстати, закомментировать строку, в которой рисуется белый прямоугольник на весь экран, то можно увидеть как здоровенная зловещая чёрная линия становится всё длиннее и длиннее - это наш чёрный прямоугольник перерисовывается каждый раз, но его предидущее положение не стирается.

Объяснение участков, требующих внимания:

Участок номер один - в начале мы инициализируем переменную х и записываем в неё значение 50 - это число символизирует координату, с которой наш чёрный прямоугольник начнёт свой путь.

Участок номер два  - это рисование чёрного прямоугольника. Теперь вместо одной из координат записана переменная x. Число, которое в неё записано, изображает координату по горизонтали, на которой он будет находиться его верхний угол каждый раз, когда его будут рисовать.

Участок номер три  - это увеличение переменной x на еденицу. Такая операция называется инкрементом и используется для того, что бы выглядеть круто. Эту строчку можно было бы записать вот в таком виде:
x = x   1;
Но мы хотим выглядеть круто и пишем инкремент.

Содержимое функции update, как мы указали ранее (строка "setInterval(update, 100);" ) , выполняется один раз в 0.1 секунду. Стёрли всё, нарисовали прямоугольник, передвинули его, стёрли, нарисовали, передвинули, стёрли, нарисовали, передвинули,..

Proydoha

26-09-2012, 00:20:30 #1 Последнее редактирование: 26-09-2012, 00:24:35 от Proydoha
FPS! Frames per second!

Ценность умения выводить FPS на экран сейчас не в умении выводить FPS, а в умении выводить текст вообще. Да, я рассказал первно-наперво как выводить текст через алерты, но дело в том, что алерт приостанавливает выполнение программы. Это здорово в одних случаях и совсем никуда не годится в других. На самом деле кроме алерта есть ещё две схожие команды, но я их не знаю.

Для того, что бы рисовать текст на канвасе/канве/холсте... кстати вот ещё почему надо читать учебники - там не будет идиотских жаргонных слов. Вместо них там будет чёткая терминология.. В рисовании текста нам помогут следующие функции:

context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";
context.fillText("FPS", canvas.width, 0);


Самая верхняя задаёт стиль всем надписям, которые будут рисоваться после неё. В данном случае там указаны только шрифт-по-умолчанию и высота в 12 пикселей. Мануал в который я смотрю прямо сейчас подсказывает:"The font property uses the same syntax as the CSS font property." Что в переводе на русский язык значит:"СПЕРВА ИЗУЧИ CSS, ДАУН!"

Та, что чуть пониже задаёт выравнивание. Выше я говорил, что первые два параметра в функции, рисующей прямоугольники, задают позицию ВЕРХНЕГО ЛЕВОГО УГЛА прямоугольнка. Тут можно указать какой угол будет главным. Так как FPS все крутые разработчики игр пишут в правом верхнем углу - мы выравнивание установим по правой стороне.

Ещё ниже - та же ерунда, только по вертикали. Я написал "top". Если объеденить с предидущей командой выйдет, что рисовать мы будем относительно верхней правой точки текста.

Собственно следующая функция отвечает за рисование (там написано fill, как я и говорил выше - "заливка") самих букв. Так как само число ещё надо подсчитать я просто вписал туда буквы "FPS" и приказал нарисовать в верхнем правом углу экрана.

Готовый пример:

var canvas;
var context;

var x = 50;

function update()
{
context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,30,10,20);
x++;

context.font = "12px sans-serif";          //  <<----------------------------- всё внимание сюда
context.textAlign = "right";               //  <<----------------------------- всё внимание сюда
context.textBaseline = "top";              //  <<----------------------------- всё внимание сюда
context.fillText("FPS", canvas.width, 0);  //  <<----------------------------- всё внимание сюда

}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 100);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Если обновить страницу можно заметить, что в правом верхнем углу экрана появилась надпись "FPS".

Теперь подсчитаем количество кадров в секунду. Да, ниже по тексту мы сказали, что хотим обновлять экран (вызывать функцию update) раз в 0.1 секунду. Соответственно FPS = 10. Но на деле он может отличаться и о наличии этой разницы надо знать, что бы устранить её.

Что надо делать, что бы узнать FPS? Надо засечь время между вызовами функции update. Заготовим под это дело две переменных: thisTime и pervTime.

perv - это от слова previous (предыдущий), а не от слова pervert(извращенец).

А теперь "внимание на экран":

var canvas;
var context;

var thisTime = new Date().getTime(); //  <<----------------------------- всё внимание сюда
var pervTime = thisTime;             //  <<----------------------------- всё внимание сюда

var x = 50;

function update()
{
thisTime = new Date().getTime();                         //  <<----------------------------- всё внимание сюда
var diffTime = Math.floor(1000 / (thisTime - pervTime)); //  <<----------------------------- всё внимание сюда

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,30,10,20);
x++;

context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0);  //  <<----------------------------- всё внимание сюда

pervTime = thisTime;                          //  <<----------------------------- всё внимание сюда
}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 100);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Сразу много важных моментов. Например: что делает "new Date().getTime();" ?
В дажаваскрипте есть целый набор супер-важных объектов: Array, Date (<--вот наш), Math, String.
На самом деле есть и ещё, но о них думать пока не стоит.

Прочитать про все важные объекты джаваскритпа можно тут: http://www.w3schools.com/jsref/default.asp

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

Умение объекта Date().getTime() возвращает количество милисекунд, которое прошло от начала времён (от первого января 1970 года).

Можно узнать месяц( getMonth() ), или год( getFullYear() ), или, там, я не знаю, тупо время ( getHours(), getMinutes(), setSeconds() ). Но мы узнали количество милисекунд от начала времён ( getTime() ).

Теперь разглядываем функцию update. Там в начале сразу три важных точки. Перво-наперво засекаем время.
В следующей строке можно заметить как я инициализирую переменную diffTime. Если инициализировать переменные внутри функций, то они будут существовать только внутри функций. Это называется локальные переменные.

И сразу во время её инициализации я присваиваю ей значение: ОДНА ТЫСЯЧА (это одна секунда) РАЗДЕЛИТЬ НА РАЗНИЦУ МЕЖДУ ДВУМЯ ОТСЧЁТАМИ ВРЕМЕНИ. Ну, два отсчёта. Один раз отсчитали в этот вызов функции update. Один раз в предидущий.

Math.floor(какое-то число). СМОТРИТЕ! Вот ещё один супер-важный объект. В этом хранятся всякие важные шутки для вычислений. Синусы, там, косинусы, всё такое.
Полный перечень умений объекта Math: http://www.w3schools.com/jsref/jsref_obj_math.asp
Очень, очень важные и нужые штуки. Что бы не ходить каждый раз по ссылке рекоммендую вытатуировать содержимое той страницы прямо у себя на руке.

Math.floor, кстати, выполняет "округление вниз". То есть отбрасывает дробную часть от чисел.

Зачем делить тысячу на разницу между временами? Если никто ещё не догадался - разница между вызовами это количество милисекунд между двумя вызовами. Что бы узнать сколько раз это время поместится в одну секунду (тысячу милисекунд) надо её (секунду) разделить на эту разницу.

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

Почему я это сделал именно в самом конце? Потому что если вдруг походу функции update что-то пойдёт не так - счётчик fps упадёт в ноль.

Последнее замечание по кадрам в секунду: я подсмотрел такой трюк, позволяющий иметь быстрый и лёгкий доступ к регулятору количества кадров.

Внимание на экран:

var canvas;
var context;

var thisTime = new Date().getTime();
var pervTime = thisTime;

var FPS = 30;  //  <<----------------------------- всё внимание сюда, это тот самый регулятор

var x = 50;

function update()
{
thisTime = new Date().getTime();                       
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,30,10,20);
x++;

context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0);

pervTime = thisTime;
}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");
  setInterval(update, 1000/FPS);   //  <<----------------------------- всё внимание сюда
}
else
{
  alert("Не получилось получить контекст!");
}
}


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

Я поставил 30 fps как тру-wannabe-консольщик. Мне нравятся два числа: 30 и 100, но 100 - это местами очень быстро.

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

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

Ловить нажатие клавиш и другие следы нашего взаимодействия с программой будем при помощи событий.
Самые главные события (ивенты), которые нам понадобятся: на нажатие кнопки мышки, на отпускание кнопки мышки, на перемещение мышки, на нажатие любой клавиши клавиатуры и, соответственно, на отпускание любой клавиши клавиатуры.

Один ивент, которого нету среди перечисленных, кстати, в нашу программу уже записан. Это тот самый, который срабатывает когда страница загружается полностью. Допишем к нему его друзей:


var canvas;
var context;

var thisTime = new Date().getTime();
var pervTime = thisTime;  //Как мне сообщили я безграмотный кретин и "предидущий" будет "previous".
                          //В то время как "pervious" переводится как "проницаемый", "поддающийся влиянию"

var mousePressed = false; //  <<------ всё внимание сюда, я заготавливаю переменные, в которых будет важная инфа
var mouseX = 0;           //В этих двух - координаты мышки ВНУТРИ ПОЛЯ ДЛЯ РИСОВАНИЯ
var mouseY = 0;           //В предидущей информация о том нажата ли она
var keys   = new Array(); //А это массив кнопок. Каждый элемент массива будет отображать нажата кнопка или нет.

//Я про массивы не рассказал, но если в двух словах - это как упорядоченное хранилище похожих штук. Например -
//есть такие штуки квадратные, в которых яйца хранятся. Каждое яйцо - это элемент двухмерного массива яиц.
//Если эти штуки положить друг над другом, то массив получится трёхмерный. Если оставить только один ряд яиц - то он
//одномерный. Этот - одномерный. Например каждое яйцо хранит информацию о цвете - оно оранжевое или белое.
//Похожая еруда происходит и тут - каждый элемент хранит (ещё не хранит, но будет это делать) информацию о том
//нажата кнопка или нет.

var FPS = 30;

var x = 50;

function update()
{
thisTime = new Date().getTime();                       
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,30,10,20);
x++;

context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0);

pervTime = thisTime;
}

window.onload = function()
{
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");


  canvas.onmousemove = function(e)            // <<------ всё внимание сюда. Я вешаю на событие движения мышки НАД
  {                                           //ХОЛСТОМ функцию, в которой записываю её координаты относительно этого
   mouseX = e.clientX - canvas.offsetLeft - 1;//холста в переменную, инициализированную выше. Я пришел к такой
   mouseY = e.clientY - canvas.offsetTop  - 1;//формуле, она работает шикарно и о ней не надо думать.
  }                                           
  canvas.onmousedown = function(e)            //Как только нажали на кнопку мышки - считаем, что она нажата всё время
  {
   mousePressed = true;
  }
  canvas.onmouseup = function(e)              //Как только отпустили её - считаем, что она не нажата всё время
  {
   mousePressed = false;
  }

  for (i=0;i<225;i++) {keys[i] = false;}   //Тут выставляем весь массив кнопок в состояние "не нажата"
  document.onkeydown = function(e)         //Тут вешаем на событие нажатия кнопки запоминание того, что она нажата
  {
   keys[e.which] = true;                   //Такая запись позволит потом писать if (keys[номер кнопки]) { действие; }
  }
  document.onkeyup = function(e)           //То же самое на отпускание кнопки.
  {
   keys[e.which] = false;
  }

  setInterval(update, 1000/FPS);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Оукей. Выведем информацию о координатах указателя мышки на экран. Я всегда вывожу её в левый верхний угол.

Сразу без передышки новый готовый пример с массой комментариев!!!

var canvas;
var context;

var thisTime = new Date().getTime();
var pervTime = thisTime;

var mousePressed = false;
var mouseX = 0;
var mouseY = 0;
var keys   = new Array();

var FPS = 30;

var x = 50;

function update()
{
var i; // <<------------------------------------------------Создадим локальную переменную i, для использования её
thisTime = new Date().getTime();                          //в цикле for.
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,30,10,20);
x++;

context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0); 

context.textAlign = "left"; //  <<---------- всё внимание сюда. Теперь я хочу выравнивать текст по левому краю, что
                             //бы не думать сколько же пикселей займёт моя строка и какой же отступ слева мне делать

context.fillText("mouseX: "+mouseX, 0, 0*12); //Трюк на будущее: я собираюсь вывести сразу несколько строчек текста
                                               //Я знаю (я так сказал выше), что высота букв - 12 пикселей
                                               //Соответственно каждая следующая строка будет на 12 пикселей ниже
                                               //предидущей. Поэтому я буду писать 0*12, потом 1*12, потом 2*12
                                               //и так далее, что бы самому не высчитывать следующую позицию по y.
context.fillText("mouseY: "+mouseY, 0, 1*12);
context.fillText("mousePressed: "+mousePressed, 0, 2*12);

//И раз уже пошла такая пьянка, то почему бы заодно и не выводить информацию о том какие кнопки нажаты?
//Это очень просто сделать, достаточно пройтись взглядом по массиву keys и поглядеть
//какие из его элементов равны true. А номер элемента будет символизировать номер кнопки на клавиатуре.
//Что бы изучить какие номера какие кнопки символизируют можно либо загуглить "коды клавиш", либо смотреть в
//Левый угол экрана, куда мы выводим информацию о нажатых кнопках и координатах мыши.

var keysPressed = "keysPressed:  "; //Создадим спецстроку (тоже локальную переменную),
                                     //в которую будем записывать коды всех нажатых кнопок
                                     //Загадка:"Почему после двоеточия стоит два пробела, а не один?"

for (i=0;i<225;i++) //Цикл for, кстати повторяет действия, взятые в фигурные скобки и, походу дела, манипулирует
                     //переменной-итератором. В данном случае всё начинается с того, что она равна нулю (i=0)
                     //И она, с каждым новым выполнением этих действий, увеличивается на еденицу        (i++)
                     //И выполняться она будет до тех пор, пока она меньше 225                          (i<225)
                     //Итого она переберёт все 225 элементов массива keys, начиная с 0-го и заканчивая 224-ым
{
  if (keys[i])       //Если кнопка нажата, то её номер надо дописать в спецстроку
  {
   keysPressed = keysPressed + i + ", "; //Дописываем
  }
}
keysPressed = keysPressed.slice(0,keysPressed.length - 2); //Отрезаем лишний пробел и запятую от строки, которая
                                                            //получилась. Все секретные функции, для осуществления
                                                            //манипуляций над строками можно узнать вот по этой
                                                            //ссылке: www.w3schools.com/jsref/jsref_obj_string.asp

context.fillText(keysPressed, 0, 3*12); //Когда все кнопки записаны - выведем эту строку на экран

                             
pervTime = thisTime;
}

window.onload = function()
{
var i; //  <<--------------------------------дебил забыл инициализировать локальную переменную в прошлый раз отут
canvas  = document.getElementById("game"); //язык для даунов позаботился обо мне и ничего не сказал плохого
if (canvas.getContext)
{
  context = canvas.getContext("2d");


  canvas.onmousemove = function(e)           
  {                                           
   mouseX = e.clientX - canvas.offsetLeft - 1;
   mouseY = e.clientY - canvas.offsetTop  - 1;
  }                                           
  canvas.onmousedown = function(e)
  {
   mousePressed = true;
  }
  canvas.onmouseup = function(e)
  {
   mousePressed = false;
  }

  for (i=0;i<225;i++) {keys[i] = false;}
  document.onkeydown = function(e)
  {
   keys[e.which] = true;
  }
  document.onkeyup = function(e)
  {
   keys[e.which] = false;
  }

  setInterval(update, 1000/FPS);
}
else
{
  alert("Не получилось получить контекст!");
}
}



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

var canvas;
var context;

var thisTime = new Date().getTime();
var pervTime = thisTime;

var mousePressed = false;
var mouseX = 0;
var mouseY = 0;
var keys   = new Array();

var FPS = 30;

var x = 200;
var y = 200;

function update()
{
var i;
thisTime = new Date().getTime();
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x,y,50,50);

// x++; // <-----------------Я подавил волю чёрного прямоугольника

context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0); 

context.textAlign = "left";

var keysPressed = "keysPressed:  ";

for (i=0;i<225;i++) {if (keys[i]) {keysPressed = keysPressed + i + ", "; } } // <-----Я переставил это в начало,
keysPressed = keysPressed.slice(0,keysPressed.length - 2);                   // что бы программа выглядела кавайнее

context.fillText("mouseX: "+mouseX,             0, 0*12);
context.fillText("mouseY: "+mouseY,             0, 1*12);
context.fillText("mousePressed: "+mousePressed, 0, 2*12);
context.fillText(keysPressed,                   0, 3*12);

if (keys[87]) { y--; } //Я назначил на кнопки w, a, s, d перемещение брутального чёрного квадрата по экрану
if (keys[83]) { y++; }
if (keys[65]) { x--; }
if (keys[68]) { x++; }

pervTime = thisTime;
}

window.onload = function()
{
var i;
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");

  canvas.onmousemove = function(e)           
  {                                           
   mouseX = e.clientX - canvas.offsetLeft - 1;
   mouseY = e.clientY - canvas.offsetTop  - 1;
  }                                           
  canvas.onmousedown = function(e)
  {
   mousePressed = true;
  }
  canvas.onmouseup = function(e)
  {
   mousePressed = false;
  }

  for (i=0;i<225;i++) {keys[i] = false;}
  document.onkeydown = function(e)
  {
   keys[e.which] = true;
  }
  document.onkeyup = function(e)
  {
   keys[e.which] = false;
  }

  setInterval(update, 1000/FPS);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Прежде чем переходить к другим функциям рисования я хотел бы пройтись по, собственно, функциям вообще и по объектам. Начёнм с функций. С добротного, почти классического, примера, который, в отличие от тупорылых примеров из учебников, будет приносить некоторую пользу.

Как найти расстояние между двумя точками все помнят? Этому ещё в школе на уроках геометрии учат. Крайне важное умение. Для тех, кто забыл, напоминаю:

Расстояние между двумя точками = Корень квадратный из ( x1 - x2 )^2 + ( y1 - y2 )^2

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

Оформим определение расстояния между двумя точками в виде функции:

function getDist(x1,y1,x2,y2)
{
return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
}


Math.sqrt(а)   - извлекает корень из того, что в скобках
Math.pow(а,б)  - возводит а в степерь б.

Общий вид функции выглядит примерно так:

function имфункции(аргументы) _НЕТУ ТОЧКИ С ЗАПЯТОЙ_
{

какие-то действия

return то, что функция возвращает;
}


Когда я ходил в школу я недоумевал почему везде пишется, что функция "возвращает" значение. Спасибо Паскалю за годы недоумения! Это потому, что он позволял писать вместо слова ретурн имя функции, чем я и пользовался.

В следующий раз, когда я буду писать в своей программе getDist и четыре координаты-параметра в скобках, она "вернёт" мне расстояние между этими координатами. Ещё раз повторяю: крайне ценная информация.


Ещё одна функция из must have набора. Отдаю бесплатно, налетай, пока горячее:

function getAngle(x1,y1,x2,y2)
{
if ((x2 >= x1) && (y2 <= y1))
{
  return Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 <= y1))
{
  return 180 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 >= y1))
{
  return 180 + Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 >= x1) && (y2 >= y1))
{
  return 360 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
}


Определяет угол между двумя точками. Самое время сказать: "дебил, между двумя точками нельзя определить угол, потому что получится линия, а линий надо как минимум две, что бы получился угол!" Короче я в курсе, что так говорить неправильно.


_________________>
|                  x
|       .b
|      / 
|     /
|    / альфа
|   .a_____
|
|
V
  y

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

Четыре if-а определяют в какой четверти находится искомый угол.
Math.atan - определяет арктангенс.
Math.abs  - возвращает модуль от числа (отбрасывает знак минус, если он есть)

Умножение на 180/Math.PI нужно для того, что бы угол возвращался в градусах. Все или, во всяком случае, подавляющее большинство, языков программирования пользуется радианами, а я их не понимаю и получаю себе результат в градусах. Подстава в том, что при отправлении этого числа обратно в программу его надо будет умножить на ПИ и разделить на 180, что бы снова превратить в радианы.

У меня четырёхядерный процессор, я могу себе позволить эту слабость! :)

Памятка для дебила: пропорции, которые проходили в школе, крайне важны. Именно они используются при превращениях градусов в радианы и обратно.

Если информация о пропорциях из мозга стёрлась, как это у меня регулярно случается, я рекомендую вытатуировать у себя на руке следующие строки:

a = b
c = d
a*d = c*b

В случае с градусами/радианами надо помнить следующее: ПИ - это 180 градусов. ProTip: если сложно запомнить то, что ПИ - это 180 градусов, то, возможно, будет легче запомнить, что 360 градусов это 2*ПИ.

a = b, в нашем случае, это 180 = ПИ или 360 = 2*ПИ
Таким образом узнавание значения любого градуса в радианах (и наоборот) становится лёгкой задачей.
Если известны градусы и их надо превратить в радианы, то градусы надо записывать в букву c и решать уравнение с которым справится и даун.
Если известны радианы, то их надо записывать в букву d и решать уравнение с которым справится и даун.

Применение на практике. Я говорил об эмоциональном отклике и я собираюсь его немедленно усилить:

var canvas;
var context;

var thisTime = new Date().getTime();
var pervTime = thisTime;

var mousePressed = false;
var mouseX = 0;
var mouseY = 0;
var keys   = new Array();

var FPS = 100;        //Я поднял FPS до сотни в попытке понять что же не так с Оперой. С Оперой не всё в порядке.
                      //Изменения произошли после её последнего обновления.
                      //Дело в том, что по непонятой причине проседает FPS. НО ЕСЛИ ОДНОВРЕМЕННО С ОПЕРОЙ
                      //ЗАПУСТИТЬ ГУГЛ ХРОМ, ТО ЗНАЧЕНИЕ FPS СРАЗУ СТАНОВИТСЯ ТАКИМ КАК Я ЗАКАЗАЛ!!!
var x = 200;
var y = 200;
var size      = 50;   // Перво наперво я заготавливаю переменные для всего. Я хочу менять размер своего квадрата.
var breathe   = 0;    // Я хочу, что бы он дышал
var breatheint= 0;    // И эти переменные мне помогут
var inhale    = true; // Я определяю вдыхает он или нет
var eyesize   = 10;   // Я хочу, что бы у него были глаза и это их размер
var pupilsize = 2;    // Я хочу, что бы у него в глазах были зрачки и это их размер
var blink     = false;// Я хочу, что бы он моргал и эта переменная расскажет мне закрыты ли его глаза
var blinkint  = 0;    // А благодаря этой я узнаю не пора ли моргать или вымаргивать

function getDist(x1,y1,x2,y2)  //ВОТ ОНИ! ВОТ ОНИ ФУНКЦИИ МОЕЙ МЕЧТЫ!   
{
return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
}

function getAngle(x1,y1,x2,y2) //ВОТ ОНИ! ВОТ ОНИ!
{
if ((x2 >= x1) && (y2 <= y1))
{
  return Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 <= y1))
{
  return 180 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 >= y1))
{
  return 180 + Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 >= x1) && (y2 >= y1))
{
  return 360 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
}

function update()
{
var i;
thisTime = new Date().getTime();
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

context.fillStyle="#000000";
context.fillRect(x+breathe,y-breathe,size-breathe*2,size+breathe); //Я прикрутил "дыхание" к рисованию квадрата

if (blink) //Если мой квадрат моргнул, то необходимо нарисовать закрытые глаза
{          //Большую часть параметров я подбирал экспериментально, опираясь на заданные ранее значения
  context.fillStyle="#FFFFFF";
  context.fillRect(x+size/2+eyesize-eyesize/2  ,y+eyesize-breathe+eyesize/4,eyesize+eyesize/4,eyesize-eyesize/2);
  context.fillRect(x+size/2-eyesize-eyesize/1.5,y+eyesize-breathe+eyesize/4,eyesize+eyesize/4,eyesize-eyesize/2);
}
else       //Если мой квадрат выморгнул, то необходимо рисовать открытые глаза
{
  context.fillStyle="#FFFFFF"; //Белки глаз белые, я выбираю белый цвет и рисую их
                               //Механизм примерно такой: левый глаз надо нарисовать на один глаз и ещё полглаза
                               //левее, чем середина квадрата. С правым то же самое, но наоборот. Глаза квадратные
                               //то есть в последних двух параметрах рисования прямоугольника я указываю размер
                               //глаза.
  context.fillRect(x+size/2+eyesize - eyesize/2,y+eyesize - breathe,eyesize,eyesize);
  context.fillRect(x+size/2-eyesize - eyesize/2,y+eyesize - breathe,eyesize,eyesize);

  context.fillStyle="#000000"; //Зрачки чёрные, я снова выбираю чёрный цвет

  var eyeposx = x + size/2 + eyesize   - pupilsize/2;            //Я создал локальную переменную для вычисления
  var eyeposy = y + eyesize - breathe + eyesize/2 - pupilsize/2; //координат глаз, так как к ним припишется ещё
                                                                 //вычисление координат зрачков, а это тонна текста.
  context.fillRect(eyeposx + Math.cos(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,
                   eyeposy + Math.sin(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,pupilsize,pupilsize);

  //Замес, примерно, такой: глаза следят за мышкой, поэтому к икосовой "позиции зрачка" надо добавлять косинус
  //угла между мышкой и "глазом" и умножить его на "длину" на которую он должен отстоять от "середины глаза".
  //Для игрековой "позиции зрачка" всё то же самое, только синус, вместо косинуса.

  //Я сперва подобрал такие значения eyeposx, eyeposy, что бы зрачки рисовались ровно в центре глаза, а потом только
  //дописал синусы-косинусы, создающие направление взгляда.

  //
  // .----------------------->
  // |
  // |        .b
  // |  dist / |
  // |      /  |<--sin(getAngle(eyeposx,eyeposy,mouseX,mouseY))*dist
  // |     /   |
  // |    .a____
  // |       ^
  // |       |
  // | cos(getAngle(eyeposx,eyeposy,mouseX,mouseY))*dist
  // |
  // |
  // V

  //Я обнаружил, что я слажал где-то в функции getAngle и надо брать значение -getAngle, что бы всё было по фен шую
  //В качестве точки a тут координаты глаза: eyeposx, eyeposy
  //В качестве точки b тут координаты мышки: mouseX , mouseY

  //Максимальное перемещение зрачка в глазу - это перемещение на полглаза. Ещё чуть больше и он выходит за его
  //пределы. То есть, в качестве dist, можно было бы указывать eyesize/2, но тогда зрачки будут болтаться по краям
  //глаза и никогда не смотреть, например, прямо в центр экрана. Поэтому я решил сделать расстояние, на которое они
  //удаляются зависимым от расстояния курсора мышки от глаза.

  //Смотрим на свежую татуировку!
  //a=b
  //c=d
  //а - это максимальное реальное удаление зрачка от центра глаза - то есть eyesize/2
  //b - это максимальное удаление "мышки" от центра глаза. То, при котором зрачок будет у его края.
  //    Я принял это значение за 800. Просто потому что я так захотел.
  //с - это удаление зрачка от центра глаза, которое нам надо найти
  //d - это расстояние от центра глаза до мышки - то есть getDist(eyeposx,eyeposy,mouseX,mouseY).
  //a*d=c*b
  //c = a*d/b
  //c = (eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800

  //Вот как я получил такую формулу для рассчёта положения одного зрачка. Для второго всё то же самое.

  eyeposx = x + size/2 - eyesize   - pupilsize/2;
  eyeposy = y + eyesize - breathe + eyesize/2 - pupilsize/2;
  context.fillRect(eyeposx + Math.cos(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,
                   eyeposy + Math.sin(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,pupilsize,pupilsize);
}

if (breatheint % 10 == 0) //Эти моменты объяснения не должны требовать. Надо только сказать, что a % b это остаток
                           //от деления a на b. То есть когда остаток от breatheint/10 будет 0, тогда условие и true
                           //Это сделано для того, что б квадрат не дышал как негр, забирающий с собой
                           //в Вальгаллу здание корпорации Cyberdyne Systems
{
  if (inhale) { breathe++; } else { breathe--; } //Квадрат раздувается на вдохе и сдуваеься на выдохе
}
if (breathe >  3) { inhale=false; }      //Границы раздутия
if (breathe < -3) { inhale=true;  }      //Переключающие вдох на выдох и обратно

breatheint++;                            //Инкремент

if (breatheint > 500) { breatheint = 0; }//Я не даю превысить этому числу отметку 500 просто так.

blinkint++;           //Инкремент
if (blink)            //С глазами та же история, что и с дыханием, только моргает он рандомно, а вымаргивает - нет
{ if (blinkint > 50)                                   { blink = false; blinkint = 0; } }
else
{ if (blinkint > 200 + Math.round(Math.random()*1000)) { blink = true;  blinkint = 0; } }

context.fillStyle="#000000";
context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0); 

context.textAlign = "left";

var keysPressed = "keysPressed:  ";

for (i=0;i<225;i++) {if (keys[i]) {keysPressed = keysPressed + i + ", "; } }
keysPressed = keysPressed.slice(0,keysPressed.length - 2);

context.fillText("mouseX: "+mouseX,             0, 0*12);
context.fillText("mouseY: "+mouseY,             0, 1*12);
context.fillText("mousePressed: "+mousePressed, 0, 2*12);
context.fillText(keysPressed,                   0, 3*12);

if (keys[87]) { y--; }
if (keys[83]) { y++; }
if (keys[65]) { x--; }
if (keys[68]) { x++; }

pervTime = thisTime;
}

window.onload = function()
{
var i;
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");

  canvas.onmousemove = function(e)           
  {                                           
   mouseX = e.clientX - canvas.offsetLeft - 1;
   mouseY = e.clientY - canvas.offsetTop  - 1;
  }                                           
  canvas.onmousedown = function(e)
  {
   mousePressed = true;
  }
  canvas.onmouseup = function(e)
  {
   mousePressed = false;
  }

  for (i=0;i<225;i++) {keys[i] = false;}
  document.onkeydown = function(e)
  {
   keys[e.which] = true;
  }
  document.onkeyup = function(e)
  {
   keys[e.which] = false;
  }

  setInterval(update, 1000/FPS);
}
else
{
  alert("Не получилось получить контекст!");
}
}


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

Один куб хорошо, но если я захочу сделать больше, мне придётся или тронуться умом описывая каждый из них, создавая набоы переменных и страдая подобной ерундой. Или оформить их как объекты. Этим и займёмся.

Синтаксис простой:
Сперва я пишу

имя_объекта = function(параметры, которые будут задавать его начальное состояние)
{
И потом вот тут записываю его параметры, попутно приписывая им слово this. У квадрата богато параметров. Я заранее
подготовился к превращению его в объект и выписал их в начале программы. И значения присвоил.
}

Proydoha

26-09-2012, 00:22:27 #2 Последнее редактирование: 26-09-2012, 00:31:40 от Proydoha
var canvas;
var context;

var thisTime = new Date().getTime();
var pervTime = thisTime;

var mousePressed = false;
var mouseX = 0;
var mouseY = 0;
var keys   = new Array();

var FPS = 100;

blob = function(x,y)     //Вот то, о чём я говорил. Я описал объект. Назвал его blob. Потому что я назвал его так.
{
this.x = x;             //Когда я создам мой blob я присвою ему координаты. Остальные параметры не так важны.
this.y = y;             //Но я всё равно смогу изменить их. Я пришел к выводу, что лучше сделать маленькое
this.size      = 50;    //Количество входящих параметров, чем передавать в каждый новый экземпляр по 500 штук.
this.breathe   = 0;     //О том, что они делают я описал в прошлом примере.
this.breatheint= 0;     //Слова "this" всюду!
this.inhale    = true;
this.eyesize   = 10;
this.pupilsize = 2;
this.blink     = false;
this.blinkint  = 0;
}

blob.prototype.draw = function() //А с помощью такой конструкции можно прикрепить к объекту способность что-то делать
{                                //Например я всегда учу свои объекты рисовать самих себя.
context.fillStyle="#000000";    //Следовательно тут всё, что было в функции update в прошлом примере,
                                 //касающееся моего кавайного квадратика
context.fillRect(this.x+this.breathe,this.y-this.breathe,this.size-this.breathe*2,this.size+this.breathe);

if (this.blink)                 //Только ко всем параметрам дописано this.
{                               //Я повторяю это много раз потому что сам часто забываю это сделать и страшно злюсь,
  context.fillStyle="#FFFFFF";   //когда программа сообщает мне о том какой я идиот.

  context.fillRect(this.x+this.size/2+this.eyesize-this.eyesize/2  ,
        this.y+this.eyesize-this.breathe+this.eyesize/4,this.eyesize+this.eyesize/4,this.eyesize-this.eyesize/2);
  context.fillRect(this.x+this.size/2-this.eyesize-this.eyesize/1.5,
        this.y+this.eyesize-this.breathe+this.eyesize/4,this.eyesize+this.eyesize/4,this.eyesize-this.eyesize/2);
}
else
{
  context.fillStyle="#FFFFFF";
  context.fillRect(this.x+this.size/2+this.eyesize - this.eyesize/2,
                   this.y+this.eyesize - this.breathe,this.eyesize,this.eyesize);
  context.fillRect(this.x+this.size/2-this.eyesize - this.eyesize/2,
                   this.y+this.eyesize - this.breathe,this.eyesize,this.eyesize);
  context.fillStyle="#000000";
   
   
  var eyeposx = this.x + this.size/2  + this.eyesize - this.pupilsize/2; //Перед eyeposx и y нету слова this
                                                                         //Потому что это не свойства моего
                                                                         //объекта, а временные переменные
                                                                         //в которых хранятся промежуточные
                                                                         //вычисления
  var eyeposy = this.y + this.eyesize - this.breathe + this.eyesize/2 - this.pupilsize/2;
  context.fillRect(eyeposx + Math.cos(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,
                   eyeposy + Math.sin(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,this.pupilsize,this.pupilsize);
 
  var eyeposx = this.x + this.size/2  - this.eyesize - this.pupilsize/2;
  var eyeposy = this.y + this.eyesize - this.breathe + this.eyesize/2 - this.pupilsize/2;
  context.fillRect(eyeposx + Math.cos(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,
                   eyeposy + Math.sin(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,this.pupilsize,this.pupilsize);
}

if (this.breatheint % 10 == 0)
{
  if ( this.inhale) { this.breathe++; } else { this.breathe--; }
}
if (this.breathe >  3) { this.inhale=false; }
if (this.breathe < -3) { this.inhale=true; }
this.breatheint++;
if (this.breatheint > 500) { this.breatheint = 0; }

this.blinkint++;
if (this.blink)
{ if (this.blinkint > 50)                                   { this.blink = false; this.blinkint = 0; } }
else
{ if (this.blinkint > 200 + Math.round(Math.random()*1000)) { this.blink = true;  this.blinkint = 0; } }

}     //Эта скобка закрывает описание объекта. Теперь, что бы им пользоваться, надо его создать
      //Я это сделаю в функции init(), которую опишу чуть ниже и вызову перед вызовом setInterval(update,1000/FPS)


function getAngle(x1,y1,x2,y2)
{
if ((x2 >= x1) && (y2 <= y1))
{
  return Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 <= y1))
{
  return 180 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 >= y1))
{
  return 180 + Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 >= x1) && (y2 >= y1))
{
  return 360 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
}

function getDist(x1,y1,x2,y2)
{
return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
}

function update()
{
var i;
thisTime = new Date().getTime();
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

bobby.draw();     //Я рисую свои блобы. В этом нету ничего интересного.
rupert.draw();    //Я взываю к их умению нарисовать себя именно там, где раньше было описано рисование в предидущем
                   //примере.

context.fillStyle="#000000";
context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0); 

context.textAlign = "left";

var keysPressed = "keysPressed:  ";

for (i=0;i<225;i++) {if (keys[i]) {keysPressed = keysPressed + i + ", "; } }
keysPressed = keysPressed.slice(0,keysPressed.length - 2);

context.fillText("mouseX: "+mouseX,             0, 0*12);
context.fillText("mouseY: "+mouseY,             0, 1*12);
context.fillText("mousePressed: "+mousePressed, 0, 2*12);
context.fillText(keysPressed,                   0, 3*12);

// if (keys[87]) { y--; }  //временно дизактивировал кнопки управления
// if (keys[83]) { y++; }
// if (keys[65]) { x--; }
// if (keys[68]) { x++; }

pervTime = thisTime;
}

function init()              //Функция init(). Я нахожу удобным заготавливать всё в отдельной функции.
{                            //Но я не делаю этого в самом начале программы потому что там ещё не подготовлен
                             //"плацдарм" для вторжения на экраны - не загружена страница в броузере.
                             //Может это и паранойя, но мне так спокойнее.

bobby  = new blob(200,200); //Создам переменную "Бобби" и запишу в неё экземпляр объекта блоб. 200,200 - это
                             //координаты. Соответственно левая часть экрана.
rupert = new blob(800,200); //И друга ему. Для смеху друга будут звать Рупертом. 800 - правая часть экрана.

                             //Перед словами bobby и rupert нету слова var не потому, что это объекты, а потому,
                             //что тогда они станут локальными переменными в функции init() и исчезнут как только она
                             //выполнится. Что бы этого избежать можно заранее заготовить место для Бобби и Руперта,
                             //написав где-то вне функций var bobby и var rupert, но так как джаваскрипт рассчитан на
                             //дебила я этого не сделал.
}

window.onload = function()
{
var i;
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");

  canvas.onmousemove = function(e)           
  {                                           
   mouseX = e.clientX - canvas.offsetLeft - 1;
   mouseY = e.clientY - canvas.offsetTop  - 1;
  }                                           
  canvas.onmousedown = function(e)
  {
   mousePressed = true;
  }
  canvas.onmouseup = function(e)
  {
   mousePressed = false;
  }

  for (i=0;i<225;i++) {keys[i] = false;}
  document.onkeydown = function(e)
  {
   keys[e.which] = true;
  }
  document.onkeyup = function(e)
  {
   keys[e.which] = false;
  }

  init();                     //Вызов функции init(). Это важный участок, так как можно всё красиво написать,
                              //а потом рвать волосы:"ПОЧЕМУ НЕ РАБОТАЕТ?!?!?!" Да потому, что функцию ты описал,
                              //дурачина, а вызвать забыл.

  setInterval(update, 1000/FPS);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Дальше чуть-чуть отвлечённо от основной темы.

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

Перво-наперво сделаем Бобби и Руперта непохожими друг на друга и, заодно, сотрём их личности.
Тогда вместо солдат получатся духовно богатые люди, которые все как один "Я не такой как все!"
"Unique doesn't mean useful"

var canvas;       //Начинаем читать комментарии задом наперёд. Не что бы славить Сатану, а что бы двигаться в
var context;      //направлении выполнения программы. А первыми выполняются именно куски, которые в самом низу.
                  //Ну, кроме этих вот, что в самом начале.

var thisTime = new Date().getTime();
var pervTime = thisTime;

var mousePressed = false;
var mouseX = 0;
var mouseY = 0;
var keys   = new Array();

var FPS = 100;

var selected = 0; //Вот эта переменная.

blob = function(x,y)
{
this.x = x;
this.y = y;
this.size      = 40;
this.breathe   = 0;
this.breatheint= 0;
this.inhale    = true;
this.eyesize   = 10;
this.pupilsize = 2;
this.blink     = false;
this.blinkint  = 0;
}

blob.prototype.draw = function()
{
context.fillStyle="#000000";
context.fillRect(this.x+this.breathe,this.y-this.breathe,this.size-this.breathe*2,this.size+this.breathe);

if (this.blink)
{
  context.fillStyle="#FFFFFF";
  context.fillRect(this.x+this.size/2+this.eyesize-this.eyesize/2  ,
        this.y+this.eyesize-this.breathe+this.eyesize/4,this.eyesize+this.eyesize/4,this.eyesize-this.eyesize/2);
  context.fillRect(this.x+this.size/2-this.eyesize-this.eyesize/1.5,
        this.y+this.eyesize-this.breathe+this.eyesize/4,this.eyesize+this.eyesize/4,this.eyesize-this.eyesize/2);
}
else
{
  context.fillStyle="#FFFFFF";
  context.fillRect(this.x+this.size/2+this.eyesize - this.eyesize/2,
                   this.y+this.eyesize - this.breathe,this.eyesize,this.eyesize);
  context.fillRect(this.x+this.size/2-this.eyesize - this.eyesize/2,
                   this.y+this.eyesize - this.breathe,this.eyesize,this.eyesize);
  context.fillStyle="#000000";
   
   
  var eyeposx = this.x + this.size/2  + this.eyesize - this.pupilsize/2;
  var eyeposy = this.y + this.eyesize - this.breathe + this.eyesize/2 - this.pupilsize/2;
  context.fillRect(eyeposx + Math.cos(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,
                   eyeposy + Math.sin(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,this.pupilsize,this.pupilsize);
 
  var eyeposx = this.x + this.size/2  - this.eyesize - this.pupilsize/2;
  var eyeposy = this.y + this.eyesize - this.breathe + this.eyesize/2 - this.pupilsize/2;
  context.fillRect(eyeposx + Math.cos(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,
                   eyeposy + Math.sin(-getAngle(eyeposx,eyeposy,mouseX,mouseY) * Math.PI / 180)*
                  (this.eyesize/2)*getDist(eyeposx,eyeposy,mouseX,mouseY)/800,this.pupilsize,this.pupilsize);
}

if (this.breatheint % 10 == 0)
{
  if ( this.inhale) { this.breathe++; } else { this.breathe--; }
}
if (this.breathe >  3) { this.inhale=false; }
if (this.breathe < -3) { this.inhale=true; }
this.breatheint++;
if (this.breatheint > 500) { this.breatheint = 0; }

this.blinkint++;
if (this.blink)
{ if (this.blinkint > 50)                                   { this.blink = false; this.blinkint = 0; } }
else
{ if (this.blinkint > 200 + Math.round(Math.random()*1000)) { this.blink = true;  this.blinkint = 0; } }
}


function getAngle(x1,y1,x2,y2)
{
if ((x2 >= x1) && (y2 <= y1))
{
  return Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 <= y1))
{
  return 180 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 <= x1) && (y2 >= y1))
{
  return 180 + Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
if ((x2 >= x1) && (y2 >= y1))
{
  return 360 - Math.abs(Math.atan((y2 - y1)/(x2 - x1)) * 180/Math.PI);
}
}

function getDist(x1,y1,x2,y2)
{
return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
}

function update()
{
var i;
thisTime = new Date().getTime();
var diffTime = Math.floor(1000 / (thisTime - pervTime));

context.fillStyle="#FFFFFF";                     
context.fillRect(0,0,canvas.width,canvas.height);

for (i=0;i<blobs.length;i++) //Рисую я свои блобы соответственно тоже в цикле.
{                            //Размером он в их количество
  blobs[i].draw();
  context.fillStyle="#000000";//Кроме того я рисую квадрат, обозначающий, 
                              //что разум именно этого блоба сейчас подчинён.
  if (i == selected) {context.fillRect(blobs[i].x+blobs[i].size/2-5,blobs[i].y-30,10,10);}
}

context.fillStyle="#000000";
context.font = "12px sans-serif";
context.textAlign = "right";
context.textBaseline = "top";                 
context.fillText(diffTime, canvas.width, 0); 

context.textAlign = "left";

var keysPressed = "keysPressed:  ";

for (i=0;i<225;i++) {if (keys[i]) {keysPressed = keysPressed + i + ", "; } }
keysPressed = keysPressed.slice(0,keysPressed.length - 2);

context.fillText("mouseX: "+mouseX,             0, 0*12);
context.fillText("mouseY: "+mouseY,             0, 1*12);
context.fillText("mousePressed: "+mousePressed, 0, 2*12);
context.fillText(keysPressed,                   0, 3*12);

if (keys[87] ) { blobs[selected].y--; }
if (keys[83] ) { blobs[selected].y++; }
if (keys[65] ) { blobs[selected].x--; }
if (keys[68] ) { blobs[selected].x++; }
if (keys[221]) { selected++; if (selected > blobs.length-1) {selected = 0;}} //Я создал наверху переменную selected
if (keys[219]) { selected--; if (selected < 0) {selected = blobs.length-1;}} //Эта переменная отвечает за порядковый
                                  //номер гражданина, разум которого сейчас подчинён. Соответственно он не может быть
                                  //меньше ноля и больше, чем их количество минус один. Кнопками [ и ] я могу менять
                                  //этот номер. Мысль о том, что я могу с лёгкостью выбирать которого персонажа
                                  //из их массива контролировать, потрясла меня до глубины души. Это казалось гораздо
                                  //более сложной задачей, чем оказалось. Выбор проблемный - точка, о которой выше,
                                  //скачет очень быстро, так как переключение выходит по удержанию кнопки, а не ёё
                                  //нажатию. Подумать о том как это исправить может быть домашним заданием.
                                  //Элементарным до слёз.
pervTime = thisTime;
}

var blobs = new Array(); //Заготовил тёпленький одномерный массивчик для моих блобов.

function init()
{
var i;                  //Даёшь локальную i в каждую функцию!
for (i=0;i<5;i++)       //В цикле создаю пять блобов. Если я не рассказывал про способность массива засовывать в
{                       //себя элементы, то сейчас самое время. Синтаксис:
                         //имя_массива.push(элемент)
                         //Ниже я создаю блобов (безимянных = со стёртой личностью) в рандомной области экрана
                         //Зато у них есть порядковые номерки как в грустных антиутопических рассказах.
                         //Никаких больше Бобби и Рупертов. Теперь это гражданин Ноль, гражданин Один, Два, Три, и,
                         //соответственно, Четыре.

  blobs.push(new blob(60 + Math.random()*(canvas.width-120),60+Math.random()*(canvas.height-120)));
  blobs[i].size     = 40 + Math.random()*20; //Но они не хотят быть одинаковыми, одетыми в одинаковую чёрную одежду,
  blobs[i].breathe  = Math.random()*7 - 3;   //как Бобби и Руперт. Они разные. Разных размеров, у них разные глаза,
  blobs[i].eyesize  = 4 + Math.random()*7;   //они не дышат синхронно. Кто-то вдыхает, кто-то выдыхает. Кто-то
  blobs[i].blinkint = Math.random()*200;     //вморгнул, кто-то стоит с закрытыми глазами, а кто-то уже выморгнул.
  if(Math.floor(Math.random()*2) == 0) {blobs[i].inhale = false;}
                                             //Осталась лишь одна общая черта - все они смотрят туда,
                                             //куда указывает Старший Брат.
}
}

window.onload = function()
{
var i;
canvas  = document.getElementById("game");
if (canvas.getContext)
{
  context = canvas.getContext("2d");

  canvas.onmousemove = function(e)           
  {                                           
   mouseX = e.clientX - canvas.offsetLeft - 1;
   mouseY = e.clientY - canvas.offsetTop  - 1;
  }                                           
  canvas.onmousedown = function(e)
  {
   mousePressed = true;
  }
  canvas.onmouseup = function(e)
  {
   mousePressed = false;
  }

  for (i=0;i<225;i++) {keys[i] = false;}
  document.onkeydown = function(e)
  {
   keys[e.which] = true;
  }
  document.onkeyup = function(e)
  {
   keys[e.which] = false;
  }

  init();

  setInterval(update, 1000/FPS);
}
else
{
  alert("Не получилось получить контекст!");
}
}


Самое время рассказать как рисуются не только квадраты.

timelimit

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

Catcatcat

а может эту тему перенести на блог делана?

Proydoha

Цитата: timelimit от 26-09-2012, 18:52:56
в виде документа

Я тупо в блокноте набираю : )

timelimit

Цитата: Proydoha от 27-09-2012, 00:52:01
Я тупо в блокноте набираю : )
Вот этот файлик и выложить для скачивания, чтобы удобно было читать... ))
А насчет выноса на блог, вспоминаю кол-во участников конкурса программирования, маловато людей интересующихся данной темой ( у нас на форуме)...

timelimit

Хорошо бы для стимуляции интереса дать пару ссылочек на игры на данном языке...

Sasha

Цитата: timelimit от 27-09-2012, 17:48:00
Хорошо бы для стимуляции интереса дать пару ссылочек на игры на данном языке...
Скачай квест от Проидохи, дойди до 3 уровня и там будет игра) Я как раз на ней и остановился)
skype: ab.sasha

timelimit

Цитата: Sasha от 27-09-2012, 18:51:19
Скачай квест от Проидохи, дойди до 3 уровня и там будет игра) Я как раз на ней и остановился)
Как скачать от Пройдохи? ))
Честно не знаю...

Proydoha


timelimit

Цитата: Proydoha от 27-09-2012, 20:41:35
Ссылка на сообщение с квестом
Зашел на фтп, но там много файлов, что качать? ))
Спасибо за файлик с текстом уроков!!!

Proydoha

Цитата: timelimit от 27-09-2012, 21:46:38
что качать? ))

Нулевой уровень ты не прошел : )

Качать картинку из сообщения.

Цитата: timelimit от 27-09-2012, 17:48:00
Хорошо бы для стимуляции интереса дать пару ссылочек на игры на данном языке...

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

Вот кто-то изготовил на продажу движок для двухмерных джаваскриптовых игр и рекламирует его прикольными игрушками:
http://playbiolab.com/
http://www.phoboslab.org/ztype/

timelimit

Прикольные игрухи... ))
И как там успехи, кто то уже освоил основы... ))
Мда, квест не прошел... ((

Proydoha

Я недавно увидел крутое видео, которое рассказывает почти всё то, что я рассказал, за 4 минуты.

CreativeJS for Designers on Vimeo
(Вимео криво встраивается, но ссылка на видео под ним есть)

Я выполнил их инструкции и вот что у меня получилось:

Раз
Два

Можно нажимать пробел.