Jump to content

Great Rash

Expert
  • Posts

    7,974
  • Joined

  • Last visited

  • Days Won

    144

Everything posted by Great Rash

  1. Быть такого не может. Возможно просто вместо клика надо тач юзать.
  2. Шаг 3. Теперь напишем расстановку мин. Для начала расставим 5 мин. Итак внутри minesweeper.js пишем: var minesweeper = function(game) {}; minesweeper.prototype = { create: function() { console.log('%cSTATE::GAME', 'color: #fff; background: #f0f;'); // вернул нормальные размеры игровому полю this.game.boardWidth = 9; this.game.boardHeight = 11; this.game.board = this.game.add.group(); // сдвигаем группу клеток так, чтобы они были как в макете (по центру) this.game.board.x = 72; this.game.board.y = 264; // максимальное количество мин (для начала 5 штук) this.game.maxBombs = 5; this.placeCells(); // после генерации поля расставляем мины this.placeBombs(); }, placeCells: function() { var len = this.game.boardWidth * this.game.boardHeight; var x = 0; var y = 0; var cell = null; for (var i = 0; i < len; i++) { x = i % this.game.boardWidth; y = Math.floor(i / this.game.boardWidth); cell = this.game.add.sprite(x * 56, y * 56, 'cells', 10); cell.posX = x; cell.posY = y; // есть ли в этой клетке мина cell.isBomb = false; // количество мин вокруг клетки cell.numBombs = 0; // запоминаем индекс клетки в массиве cell.index = i; this.game.board.add(cell); // добавляем клетку в хеш (массив), // для того чтобы мы могли в любой момент найти её по индексу this.game.board.addТоHash(cell); } }, placeBombs: function() { // сколько мин нам ещё осталось установить var bombsToPlace = this.game.maxBombs; var cell = null; do { // получаем рандомную клетку // .getRandom() - стандартный метод для любой группы в Phaser cell = this.game.board.getRandom(); // если в ней ещё нету мины if (cell.isBomb === false) { // то ставим спрайту 9-ый кадр (картинка с миной, для наглядности) cell.frame = 9; // и отмечаем, что в данной клетке бомба cell.isBomb = true; // нам осталось расставить на одну мину меньше bombsToPlace--; } } while (bombsToPlace > 0); // повтояем цикл до тех пор пока все мины не будут расставлены } }; Вот что получилось у меня в итоге: Шаг 4. Теперь первая нетривиальная часть игры - расстановка цифр вокруг мин. В своём старинном уроке я пошел немного неправильным путём. Я циклом обходил все клетки и смотрел есть ли вокруг выбранной клетки мины, если рядом с ней есь мина, то я прибавлял к клетке единичку. Немного поразмыслив, я догадался, что гораздо быстрей будет обойти все мины, ведь их сильно меньше чем пустых клеток. В нашем тестовом случае всего 5 штук. Теперь у меня осталась еще одна тема для размышлений: как в одномерном массиве получить соседние клетки? Давайте для начала вернёмся к полю 3х3. В нашей игре оно представляет собой массив, который упрощённо выглядит так: var cells = [0, 1, 2, 3, 4, 5, 6, 7, 8]; Давайте перепишем его немного, чтобы он стал похож на игровое поле: var cells = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]; Допустим нам надо получить соседей, для клетки с индексом 4. Ну с соседями слева и справа всё просто: достаточно просто вычесть/прибавить единицу, чтобы получить соседа слева/справа. А вот с соседями сверху и снизу я сперва никак не мог догнать что делать. Подозреваю, что многие из вас не такие слоупоки как я, и уже давно догадались, что надо делать. Для тех же кто похож на меня поясню: нужно всего-лишь вычесть или прибавить к индексу клетки ширину нашего поля (в данном случае 3). Ну а с диагоналями всё снова становится элементарно. Итак распишу: клетка - 4 лево - 4 - 1 = 3 право - 4 + 1 = 5 верх - 4 - 3 = 1 низ - 4 + 3 = 7 лево верх - верх - 1 = 0 право верх - верх + 1 = 2 лево низ - низ - 1 = 6 право низ - низ + 1 = 8 Элементарная математика. Ну что ж, приступим к написанию кода: var minesweeper = function(game) {}; minesweeper.prototype = { // ... placeBombs: function() { var bombsToPlace = this.game.maxBombs; var cell = null; do { cell = this.game.board.getRandom(); if (cell.isBomb === false) { cell.frame = 9; cell.isBomb = true; // как только установили мину // сразу расставляем циферки вокруг неё this.placeNumbers(cell); bombsToPlace--; } } while (bombsToPlace > 0); }, placeNumbers: function(cell) { var i = cell.index; // верх var u = i - this.game.boardWidth; // низ var d = i + this.game.boardWidth; // лево var l = i - 1; // право var r = i + 1; // верх лево var ul = u - 1; // верх право var ur = u + 1; // низ лево var dl = d - 1; // низ право var dr = d + 1; // влево мы можем идти только если клетка не у края поля if (cell.posX > 0) { this.addNumberToCell(l); // добавляем цифру в клетку // вверх влево можно идти только если сверху от клетки что-то есть if (cell.posY > 0) { this.addNumberToCell(ul); } // вниз можно только если там есть место if (cell.posY < this.game.boardHeight - 1) { this.addNumberToCell(dl); } } if (cell.posX < this.game.boardWidth - 1) { this.addNumberToCell(r); if (cell.posY > 0) { this.addNumberToCell(ur); } if (cell.posY < this.game.boardHeight - 1) { this.addNumberToCell(dr); } } if (cell.posY > 0) { this.addNumberToCell(u); } if (cell.posY < this.game.boardHeight - 1) { this.addNumberToCell(d); } }, addNumberToCell: function(i) { var cell = null; cell = this.game.board.hash[i]; // добавляем цифру только туда где нет мины if (cell.isBomb === false) { cell.numBombs++; // для наглядности делаем цифры видимыми cell.frame = cell.numBombs - 1; } } }; Вот итог наших раздумий: А тут я увеличил количество мин:
  3. Часть 3. Генерация игрового поля и игра. Шаг 1. Перед тем как рассказать про генерацию игрового поля (или игровой доски) необходимо рассказать об одном важнейшем элементе движка Phaser - группах. Группы - это мощнейший инструмент управления массивами объектов. Если ваша игра основана на тайлах, если вам нужны несколько объектов с одинаковым поведением, то вы скорее всего не сможете обойтись без групп. Вот некоторые возможности групп: 1) позиционирование нескольких объектов одновременно 2) сортировка по определённому пользователем признаку 3) фильтрация (выборка) элементов по определённому признаку 4) итерация по элементам группы 5) навешивание обработчиков событий всем элементам группы сразу 6) возможность вкладывать группы в группы Чтобы создать группу достаточно вызвать метод game.add.group(). Шаг 2. Переходим к генерации игрового поля. Для начала начнём с простого поля 3х3 клетки. Как правило его представляют так: var gameBoard = [ [0, 0, 0], [0, 0, 0], [0, 0, 0] ]; Однако внутри группы у нас элементы располагаются в одномерном массиве, т.е. в нашем случае поле будет выглядеть так: var gameBoard = [0, 0, 0, 0, 0, 0, 0, 0, 0]; Мы можем пойти двумя путями: 1) создать два массива: первый группа клеток для отображения в игре, второй - чисто для хранения данных 2) превратить одномерный массив в двумерный В качестве тренировки, я пойду вторым путём. Для нашей маленькой игры это не критично, но заодно мы получим прирост в производительности, т.к. итерация по одномерному массиву быстрее, чем итерация по двуперному (ибо вложенные циклы - плохо). Как же нам превратить одномерный массив в двумерный? А вот взгляните на картинку: Надеюсь идея понятна? Давайте применим её на практике, откройте minesweeper.js: var minesweeper = function(game) {}; minesweeper.prototype = { create: function() { console.log('%cSTATE::GAME', 'color: #fff; background: #f0f;'); // размер поля для начала 3х3 // переменные будут доступны из любого места this.game.boardWidth = 3; this.game.boardHeight = 3; // создаём группу, для хранения клеток this.game.board = this.game.add.group(); // поле будем создавать внутри отдельной функции this.placeCells(); }, // конвертируем индекс массива в координату X indexToX: function(i) { return i % this.game.boardWidth; }, // конвертируем индекс массива в координату Y indexToY: function(i) { return Math.floor(i / this.game.boardWidth); }, placeCells: function() { // сколько всего клеток (площадь игровой доски) var len = this.game.boardWidth * this.game.boardHeight; var x = 0; var y = 0; var cell = null; for (var i = 0; i < len; i++) { x = this.indexToX(i); y = this.indexToY(i); // создаём клетку игрового поля // последний параметр - это номер кадра у спрайта (10 кадр - это закрытая клетка) cell = this.game.add.sprite(x * 56, y * 56, 'cells', 10); // запоминаем координаты клетки (чтобы не рассчитывать их каждый раз) cell.posX = x; cell.posY = y; // и добавляем клетку в группу this.game.board.add(cell); } } }; Вот и готово наше игровое поле:
  4. Угу, но ничто не мешает сделать такую вёрстку. Правда понадобится over 9000 медиа-запросов написать.
  5. Шаг 7. Теперь давайте настроим масштабирование. Для нужд масштабирования в движке Phaser есть специальный класс ScaleManager. Настраивать его нужно внутри состояния boot.js для того, чтобы на этапе загрузки (preload.js) ресурсов игра уже имела необходимый масштаб. Итак открываем файл boot.js: var boot = function(game) {}; boot.prototype = { preload: function() { }, create: function() { console.log('%cSTATE::BOOT', 'color: #fff; background: #f00;'); this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; // включаем нужный режим мастабирования this.scale.pageAlignHorizontally = true; // выравнивание канваса по центру по горизонтали this.scale.pageAlignVertically = true; // ... и по вертикали this.game.state.start('Preload'); } }; Существуют следующие режимы масштабирования: 1) Phaser.ScaleManager.EXACT_FIT - канвас растягивается на 100% по ширине и высоте (контент внутри канваса пр этом деформируется) 2) Phaser.ScaleManager.NO_SCALE - отключает масштабирование 3) Phaser.ScaleManager.RESIZE - канвас растягивается на 100% по ширине и высоте (контент при этом не деформируется, но всё, что не влезает в экран обрезается) 4) Phaser.ScaleManager.SHOW_ALL - аналог background-size: contain; из CSS, т.е. канвас масштабируется с сохранением соотношения сторон и при этом не обрезается 5) Phaser.ScaleManager.USER_SCALE - канвас масштабируется согласно правилам, заданным в методе setUserScale Очевидно, что самым простым и подходящем методом масштабирования для нас будет SHOW_ALL. Шаг 8. Теперь можно подумать о шрифтах. Давайте посмотрим на интерфейс классического сапёра: В своём проекте я решил использовать шрифт, которым написаны красные цифры. Поискав в интернетах, я нашел вот такой бесплатный шрифт. Там 4 варианта начертания, я выбрал последний: DS-DIGIB.ttf (жирный без курсива). Зайдя на страницу с примерами, можно увидеть раздел про то как Phaser работает с текстом. Самым удобным для подключения шрифта мне показался вариант с подключением пиксельного шрифта (Bitmap Font). Для генерации пиксельного шрифта существуют специализированные программы для Windows и MacOS, но есть и совершенно бесплатный онлайн-генератор Littera (к слову, рекомендованный к использованию разработчиками фазера). Используя генератор Littera, я сгенерировал шрифт. Вы можете поэкспериментировать с генерацией самостоятельно или просто забрать готовый архив, который я для вас сделал. А вот такие настройки я использовал: В папке assets создайте папку font и распакуйте туда архив со шрифтом. Шаг 9. Теперь можно начинать делать экран загрузки, для которого я сделал супер крутой макет: 10 минусов снизу - это наш индикатор загрузки. Он будет заполняться по мере загрузки ресурсов. Чтобы вы не заморачивались, я приложу для вас готовую картинку: После того как скачаете её, не забудьте переименовать картинку в loading_bar.png и переложить её в папку assets/img. Затем снова откройте boot.js: var boot = function(game) {}; boot.prototype = { preload: function() { // загружаем картинку полосы загрузки this.game.load.image('loading', 'assets/img/loading_bar.png'); // и шрифт для надписи LOADING this.game.load.bitmapFont('ds_digital', 'assets/font/ds_digital.png', 'assets/font/ds_digital.fnt'); }, create: function() { console.log('%cSTATE::BOOT', 'color: #fff; background: #f00;'); this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; this.scale.pageAlignHorizontally = true; this.scale.pageAlignVertically = true; this.game.state.start('Preload'); } }; Затем откройте файл preload.js: var preload = function(game) {}; preload.prototype = { preload: function() { // Вставляем спрайт полоски загрузки var loadingBar = this.game.add.sprite(154, 588, 'loading'); loadingBar.anchor.setTo(0, 0.5); // говорим фазеру, какую картинку использовать для отображения процесса загрузки this.game.load.setPreloadSprite(loadingBar); // Вставляем текст LOADING // обратите внимание на то что написан он в нижнем регистре // ибо буквы для верхнего регистра я не генерил (в шрифте его всё равно нет), // а так спрайт шрифта весит существенно меньше var loadingText = this.game.add.bitmapText(320, 525, 'ds_digital', 'loading', 72); loadingText.anchor.setTo(0.5, 0); // Я навставлял рандомных картинок из гугла (большие панорамные изображения) // чтобы процесс загрузки был виден, иначе вы бы просто не увидели экран загрузки // т.к. все наши ресурсы весят всего пару килобайт this.game.load.image('test_0', 'https://upload.wikimedia.org/wikipedia/commons/c/cd/View_from_connors_hill_panorama.jpg'); this.game.load.image('test_1', 'http://photoblogstop.com/wp-content/uploads/2012/07/Sierra_HDR_Panorama_DFX8048_2280x819_Q40_wm_mini.jpg'); this.game.load.image('test_2', 'http://www.larkinweb.co.uk/panoramas/lake_placid/Lake_Placid_south_medium_res_panorama.jpg'); this.game.load.image('test_3', 'http://peraalto.se/wp-content/uploads/2013/03/PanoramaRaps.jpg'); this.game.load.image('test_4', 'http://parkerlab.bio.uci.edu/pictures/photography%20pictures/2008_12_19_select/Untitled_Panorama1.jpg'); // Загружаем ресурсы игры this.game.load.spritesheet('cells', 'assets/img/cells.png', 56, 56, 12); }, create: function() { console.log('%cSTATE::PRELOAD', 'color: #fff; background: #0f0;'); this.game.state.start('Game'); } }; .anchor - это якорь спрайта (текста), т.е. это точка относительно которой будет позиционироваться спрайт (или текст). Что такое якорь наглядней всего показано в этой демонстрации - спрайт позиционируется относительно зелёной точки, которая расположена в координатах 300, 300. Если вы теперь обновите страницу, то вы увидите сперва надпись LOADING, а затем наше игровое поле, состоящее пока из одной клетки.
  6. Всегда будет возвращаться (2 + 3) * 5 = 25. Потому что c не будет сохраняться в замыкании ибо LexicalEnvironment убьёт сборщик мусора (c больше нигде не нужна).
  7. Шаг 6. Давайте попробуем загрузить в нашу игру спрайт. Откройте файл preload.js и напишите код загрузки спрайта: var preload = function(game) {}; preload.prototype = { preload: function() { // Setup loading bar // ... // Load all game assets this.game.load.spritesheet('cells', 'assets/img/cells.png', 56, 56, 12); }, create: function() { console.log('%cSTATE::PRELOAD', 'color: #fff; background: #0f0;'); this.game.state.start('Game'); } }; Параметры this.game.load.spritesheet: 1) ключ - идентификатор спрайта, по нему игра будет получать доступ к спрайту 2) url - путь до ресурса 3) ширина кадра 4) высота кадра 5) количество кадров - если вы помните у нас 12 разновидностей клеток, этот параметр необязательный, если его не указать, то фазер сам попытается расчитать количество кадров в спрайте Теперь надо добавить загруженный спрайт в игру. Для этого откройте основной скрипт игры - minesweeper.js и напишите код добавления спрайта: var minesweeper = function(game) {}; minesweeper.prototype = { create: function() { console.log('%cSTATE::GAME', 'color: #fff; background: #f0f;'); // добавим спрайт 'cells' в координаты 0, 0 (верхний левый угол) this.game.add.sprite(0, 0, 'cells'); } }; Если вы, как и я, попробуете открыть файл index.html просто с харда, то вас ожидает сюрприз: ожидаемой картинки вы не увидите, зато вместо этого в консоли выскочит ошибка: Дело в том, что политика безопасности хрома не позволяет грузить ресурсы с урла вида file:///. Чтобы избавиться от этой ошибки вам понадобится web-сервер. Этим сервером может быть WAMP, Endels (Духовный наследник Denwer) или что-то иное. Но я пошел самым простым путём: установил себе Node.js. После того как инсталляция завершилась, я нажал на клавиатуре комбинацию Win+R и в появившемся окне ввёл cmd, после нажатия Enter у меня открылась командная строка где я ввёл следующую команду: npm install http-server -g -g означает, что пакет http-server будет установлен глобально, т.е. вам не придётся заходить в папку с программой чтобы запустить её, вместо этого программу можно будет запускать из любого места. После этого нужно переключиться на папку где у вас лежит ваш файл index.html, для этого в консоли пишем: cd Tutorial А теперь запускаем сервер, для этого в консоли пишем следующее: http-server -o --cors -o означает, что после запуска сервера автоматом откроется браузер --cors означает, что будет включен CORS Вот теперь вы увидите наш спрайт:
  8. Адаптивная вёрстка 80-го уровня.
  9. Шаг 1. Давайте проверим работает ли в нашем браузере игра (я использую Chrome последней версии). Для этого нам нужно вызвать конструктор Phaser.Game (часть кода я опущу для краткости): <!-- ... --> <body> <script> (function() { var game = new Phaser.Game(640, 1136, Phaser.AUTO); })(); // функция вызывает сама себя </script> </body> <!-- ... --> Параметры Phaser.Game: 1) ширина канваса 2) высота канваса 3) рендерер (можно не задавать). AUTO - означает, что фазер сам определит использовать WebGL или нет. После этого откройте консоль браузера (F12 на Windows) и проверьте, что у вас есть подобная строка: Если вы увидели на странице чёрный прямоугольник, а в консоли подобное сообщение и никаких ошибок, значит всё работает. Шаг 2. Теперь настало время поговорить про игровые состояния (States). Вообще мне кажется, что тут больше подходит термин сцена (который кстати используется в движке Unity 3D), пожалуй описание сцены больше подходит под термин "игрового состояния" в движке Phaser. Поэтому я приведу тут выдержку из документации Unity: Для управления игровыми состояниями в движке Phaser есть объект StateManager, к которому мы имеем доступ через game.state. Для каждого состояния есть предопределённый набор зарезервированных функций, которые выполняются в строго определённом порядке: 1) preload - вызывается саамой первой, в ней загружаются все ресурсы для вашей игры 2) loadUpdate - вызывается во время работы preload, нужна для обновления счётчика/ползунка загрузки 3) loadRender - нукжна для рендера счётчика/ползунка загрузки 4) create - вызывается как только отработает функция preload (обычно тут создаются все объекты необходимые на текущей сцене) 5) update - вызывается каждый кадр, в идеале 60 раз в секунду (собственно здесь работает вся механика игры) 6) render - вызывается после рендера в канвасе или WebGL, нужна для постэффектов или дебажной информации 7) resize - вызывается только если включен режим масштабирования и размеры окна просмотра меняются 8) shutdown - вызывается когда мы переключается на другое игровое состояние (на другую сцену) Для начала давайте сделаем три состояния: экран загрузки лоадера, сам лоадер и собственно игру. Для этого в папке assets/js создайте три файла: boot.js, preload.js и minesweeper.js: boot.js var boot = function(game) {}; boot.prototype = { preload: function() { // тут будем загружать каринку лоадера }, create: function() { // отладочная информация, если в консоли появилась эта надпись, // значит сцена создана успешно // %с - означает, что к инфе в консоли будут применены стили // которые передаются во втором параметре console.log('%cSTATE::BOOT', 'color: #fff; background: #f00;'); // запускаем следующую сцену (прелоадер) this.game.state.start('Preload'); } }; preload.js var preload = function(game) {}; preload.prototype = { preload: function() { // тут будем загружать спрайты для нашей игры // заодно тут мы инициалиализируем счётчи загрузки }, create: function() { // отладочная информация, если в консоли появилась эта надпись, // значит сцена создана успешно console.log('%cSTATE::PRELOAD', 'color: #fff; background: #0f0;'); // запускаем следующую сцену (игру) this.game.state.start('Game'); } }; minesweeper.js var minesweeper = function(game) {}; minesweeper.prototype = { // preload не нужна ибо загружать нам тут уже ничего не надо create: function() { // отладочная информация, если в консоли появилась эта надпись, // значит сцена создана успешно console.log('%cSTATE::GAME', 'color: #fff; background: #f0f;'); // тут будут создаваться объекты для игры (например игровое поле) } }; Затем подключаем созданные скрипты в наш index.html: <!-- ... --> <head> <meta charset="UTF-8" /> <title>Minesweeper</title> <style> body { margin: 0; } </style> <!-- Game Engine --> <script src="assets/js/phaser.min.js"></script> <!-- Game States --> <script src="assets/js/boot.js"></script> <script src="assets/js/preload.js"></script> <script src="assets/js/minesweeper.js"></script> </head> <body> <script> (function() { var game = new Phaser.Game(640, 1136, Phaser.AUTO); // добавляем в игру созданные сцены (игровые состояния) game.state.add('Boot', boot); game.state.add('Preload', preload); game.state.add('Game', minesweeper); // запускаем первое состояние game.state.start('Boot'); })(); // функция вызывает сама себя </script> </body> <!-- ... --> Параметры метода .add: 1) идентификатор состояния 2) объект состояния (они у нас в отдельных файлах скриптов) 3) автозапуск состояния (необязательный параметр, по умолчанию выключен), запускает состояние автоматом как только встречает его в коде Если вы всё сделали правильно, то в консоли вы увидите следующее: Поздравляю, наша игра работает
  10. Шаг 4. Теперь будем готовить спрайт для собственно игры. Создайте новый макет (CTRL+N) с размерами 672х56 пикселей. 56 потому, что это высота клетки, а 672 потому что вариаций клеток у нас 12 штрук, если считать пустую (56 * 12 = 672). Для удобства позиционирования измените размер грида до 56х56 (см. предыдущие шаги), жмём Edit > Preferences > Guides, Grids & Slices... и в поле Gridline Every вводим 56. Далее копируем все клетки по очереди, для удобства копирования можно на предыдущем макете слить слои в один (Layer > Merge Visible). Вот что получилось у меня: Мина, флажок и пустая клетка находятся в конце потому что так будет удобней раздавать индексы клеткам: единица будет означать едницу, двойка - двойку и т.п., индекс 12 будет означать пустую клетку. Часть 2: устанавливаем Phaser. Phaser - это игровой HTML5-движок, написанный на JS, который использует в качестве рендера другой движок Pixi. В принципе можно написать игру используя только Pixi, но поверьте, с фазером всё становится гораздо проще. Там где это возможно, Phaser использует для рендера WebGL, если браузер не поддерживает эту технологию, то используется тупо Canvas с 2D-контекстом. Тем, кто первый раз слышит про Phaser рекомендую пролистать примеры того, что на нём можно сделать. Самым большим минусом этого движка я считаю неудобную документацию, поиск по которой может быть очень затруднительным. Однако этот недостаток компенсируется отличным коммюнити (если вы понимаете английский конечно), мало того, на форуме частенько даёт ответы автор движка. Фазер из коробки имеет три физических движка: Arcade Physics, Ninja Physics и P2 Physics. Arcade Physics идеален для простых игр, если вам требуется обработка коллизий AABB, то это то что вам нужно. Ninja Physics идеален для платформеров и других игр, основанных на тайлах. P2 Physics - это уже полноценный опенсорсный физический движок, альтернатива известному Box2D, у него есть отдельный репозиторий на гитхабе, если вы решили делать клона Angry Birds, то это ваш выбор. Если вам этого показалось мало, то за деньги можно приобрести плагин, добавляющий поддержку Box2D. Для установки движка Phaser вам достаточно зайти на страницу закачки и либо клонировать проект с гитхаба, либо скачать архив, либо просто скачать файл .js (минифицированный или обычный). Я предпочитаю последний вариант, вы же делайте как привыкли. После того как вы закачали файл, подключаем его обычным способом: Моя структура файлов: Game ---- assets ----------- js ----------- img ---- index.html index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Minesweeper</title> <style> body { margin: 0; } </style> <!-- Game Engine --> <script src="assets/js/phaser.min.js"></script> </head> <body> </body> </html>
  11. Шаг 3. Теперь добавим цвета. Я не стал ничего изобретать, а просто зашел в поиск по картинкам гугла и вбил запрос "minesweeper number colors", первая же картинка мне идеально подошла. Открываем её в фотошопе и вырезаем клетку. Для этого выберите инструмент прямоугольного выделения (M), выделите клетку (ширина и высота выделения у меня получились 128х128) и скопируйте выделение (CTRL+C если у вас винда). Затем переключитесь назад на наш макет и вставьте скопированное (CTRL+V). Теперь макет должен выглядеть так: Очевидно, что клетка слишком большая, нам нужно её уменьшить. Для этого нажмите CTRL+T или выберите в меню Edit > Transform > Scale. Чтобы при уменьшении не потерялся пиксельный стиль клетки, в меню трансформации в выпадающем меню интерполяции выберите режим Nearest Neighbor. Уменьшите клетку до размера 56х56 пикселей и перетащите её в верхний левый угол нашего поля. Такой размер я выбрал потому, что в сапёре всё-таки нет зазоров между клетками и я тоже решил их не делать. После всех этих операций надобавляйте оставшихся клеток, копируя получившуюся, слейте полученные слои (Layer > Merge Down или CTRL+E) и переименуйте слой с клетками в "Cells": Теперь вернёмся к документу с картинкой из гугла и вырешем иконки флажка и цифр. Для этого воспользуемся инструментом Magic Wand (волшебная палочка - W). В настройках палочки снимите галку с чекбокса Anti-alias и установисе галку в чекбокс Contiguous: Иконки флажка и мины - двухцветные, поэтому кликать палочкой на них нужно зажав Shift, чтобы добавлять выделение к уже существующему. Выделите флажок и вырежте его на наше игровое поле (так же как клетку). После этого уменьшите его (CTRL+T). Чтобы сохранить пропорции нужно включить опцию Maintain aspect ratio. Я уменьшил иконку до сорока процентов от первоначального размера. Переместите иконку в левый верхний угол игрового поля. И проделайте всё тоже самое для остальных иконок. Теперь наш документ выглядит так: Ссылка на макет.
  12. http://htmlforum.ru/index.php?showtopic=55558
  13. Делаем сапёра на движке Phaser. Часть 1: подготовка графики. Вообще говоря любая игра обязана начинаться с тетрадки. Описывая игру в тетрадке вы понимаете чего вы хотите добиться от игры и набрасываете план действий. Все мастодонты инди-игр советуют начинать именно с этого шага и я бы не стал им пренебрегать. С другой стороны когда мы делаем клон игры (особенно такой простой как сапёр), можно смело пропускать "этап тетрадки" и сразу начать придумывать внешний вид. Ведь это гораздо интересней Как я и обещал, я постараюсь описывать создание игры в мельчайших подробностях. Поэтому первым делом расчехляйте свои фотошопы или гимпы. Предупреждаю сразу, что я пользуюсь фотошопом версии CC 2014, так что у кого гимп тем придётся разбираться самим как там всё устроено. Шаг 1. Открываем фотошоп и создаём новый файл, единицы измерения пиксели, размер 640х1136: Почему такой размер? Потому что я решил делать мобильную игру. На своём опыте я выяснил, что самое "безопасное" разрешение для большинства смартфонов - это разрешение iPhone 5. Теперь откройте браузер Google Chrome и откройте средства разработчика (F12 если вы на Windows). Внизу вы увидите иконку смартфона с подсказкой Toggle device mode, после того кк вы кликните на неё экран уменьшится до размеров устройства, которое можно выбрать в выпадающем меню сверху, вам нужно найти в этом меню опцию Apple iPhone 5: Но ведь там 320х568? Да, но рядом вы можете заметить цифру 2, которая означает плотность пикселей выбранного устройтсва. Если бы мы сделали макет размером 320х568, то на телефоне все картинки выглядели бы размытыми. Чтобы добиться чёткости на необходимо создать макет в два раза больше по размерам: 640 (320 * 2) на 1136 (568 * 2). Шаг 2. Я верстальщик, поэтому я всей душой ненавижу абсолютно всех дизайнеров с которыми мне доводилось работать. Всё из-за того, что ни один из тех с кем мне довелось работать за свою десятилетнюю карьеру понятия не имел о сетках (а тем более о модульных сетках). Давайте не будем уподобляться плохим дизайнерам и сделаем всё красиво. Если вы хотите подробней узнать о том, что такое сетки (grids), то посмотрите эту презентацию. Я начинал своё знакомство с сетками именно с неё. Как выясняется, многие не знают, что такое модульные сетки (modular grids), и путают их с обычными сетками. Это разные вещи. Совсем недавно я нашел великолепную статью о модульных сетках. Так же могу порекомендовать вам замечательный конструктор модульных сеток. Сразу хочу сказать, что модульные сетки лучше всего подходят для мобильных сайтов и приложений. Мы с вами не будем углубляться в дебри проектирования интерфейсов, однако мы почерпнём одну очень важную для нашего проекта деталь из гайдлайнов самого гугла. А именно минимальный размер модуля - 8 пикселей. Оказывается, что разрешение которое мы выбрали (640х1136) идеально делится на 8 (что по ширине, что по высоте). Поэтому откройте в фотошопе ваш файл и зайдите в настройки сетки: в верхнем меню выберите Edit > Preferences > Guides, Grids & Slices... Затем, в меню сверху выберите View > Show > Grid (или нажмите CTRL+' если у вас Windows), теперь ваш документ будет выглядеть так: Теперь вам надо найти центр вашего макета. Наврядли кто-то из сдесь присутсвующих макеты делает, но если что, то я рекомендую это делать всегда. Для этого вам надо убедиться что у вас включено отображение линеек (rulers, это те которые сверху и справа у окна макета). Выберите в меню сверху View > Rulers (или просто нажмите CTRL+R если вы на Windows). Убедитесь, что единицы измерения у вас выставлены как пиксели (по умолчанию там сантиметры вроде), для этого кликните правой кнопкой мышки по любой линейке и выберите в выпадающем меню пиксели. Затем убедитесь, что у вас включена привязка (snap), для этого в вехнем меню выберите View > Snap To > Document Bounds. После всех настроек можете смело начинать находить центр макета. Для этого кликните левой кнопкой мыши по верхней линейке и, не отпуская кнопки, потяните мышку вниз. У вас должен появиться "гайдлайн": Если вы будете тянуть его достаточно медленно, то увидите как он "прилипнет" к середине макета (сработала привязка). Проделайте ту же операцию с левой линейкой. Ваш докумен теперь должен выглядеть так: Самое главное в сапёре - клетки. В тех же гайдлайнах гугла сказано, что идеальный размер иконки на смартфоне 48х48 пикселей. Почему, спросите вы? Гугл объясняет это очень просто - такую площадь занимает пятно контакта указательного пальца у среднестатистического пользователя. "Умно!", - подумал я когда впервые об этом прочитал. Так почему бы и нам не сделать размер клетки 48х48? Создайте новый слой, выбрав в верхнем меню Layer > New > Layer..., или просто кликнув по иконке на палитре слоёв: Затем переименуйте новый слой в "Helper Cells". Далее выберите инструметн прямоугольного выделения (Rectangular Marquee Tool - шорткат M). Чтобы выделение получилось квадратным, а не прямоугольным, зажмите Shift перед тем как тянуть. Выделите в любом месте экрана прямоугольник размером 48х48 пикселей. Если у вас до сих пор активен инструмент прямоугольного выделения, то вы сможете перетаскивать выделение. Перетащите его в центр макета. В форошопе CC 2014 гайдлайны при этом должны стать фиолетовыми (так вы поймёте, что попали в центр). После этого залейте выделение любым цветом (я залил чёрным): После этого я начал экспериментировать с количеством клеток. Экспериментальным путём я выяснил, что красиво получается если добавить ещё по 4 клетки справа и слева. Но только не вплотную, а с отступом в один модуль (мы помним, что размер модуля у нас минимален - 8px). Абсолютно через такие же эксперименты, я надобавлял клеток сверху и снизу: Вот и готово наше игровое поле Нужно убегать домой. Продолжение следует (после праздников). Надеюсь я вас заинтриговал. Для ленивых.
  14. Это всё прекрасно, но не видно где вы пытаетесь навесить событие выбора radio.
  15. У вас там и не должно ничего работать. Вы же внутри функции ничего не делаете. Да и функцию саму не вызываете.
  16. А хрен его знает почему так. Если вам интересно, то я могу в течении дня выложить всё это дело на codepen например. Вообще там всё довольно печально в топике. С тех пор мой скилл в JS сильно подрос. Мало того, я теперь в игровой компании работаю (правда над интерфейсами, т.е. всё равно верстальщик ), но могу например поднатужиться и выкатить туториал сапёра скажем на движке Phaser, от и до. Если интересно пишите.
  17. http://codepen.io/GreatRash/pen/NNqNOm- так должно быть понятно
  18. В этом случае никак. Для начала вам надо определиться под какие браузеры вы хотите получить работоспособность. А затем, в зависимости от того какие вы выбрали использовать: 1) display: table;/display: table-cell; 2) display: flex; 3) JavaScript 4) хаки
  19. Замыкания - это очень просто. Посмотрите на такую функцию: function myFunction(a, { var sum = a + b; return sum;}На самом деле внутри у неё, скрытно от вас, создался невидимый объект, в котором хранится переменная sum. Т.е. для интерпретатора (в нашем случае интерпретатор - это браузер) функция выглядит вот так: function myFunction(a, { invisibleObject.sum = a + b; return invisibleObject.sum;}После того как функция отработала вступает в дело "сборщик мусора", который освобождает память на компе, которую занял этот самый invisibleObject, чтобы у вас браузер и вообще винда не тормозили. Так вот, замыкание - это когда сборщик мусора этот самый invisibleObject не удаляет. Классический пример: var counter;function myFunction() { var start = 0; counter = function() { return start++; };}myFunction(); // вызываем функцию, чтобы инициализировать переменную counter// далее юзаем уже countercounter(); // 0counter(); // 1counter(); // 2 и т.д.Браузер не дурак и видит, что переменная start может ещё понадобится в будущем, так как она используется внутри counter, поэтому он этот самый infisibleObject после того как мы вызвали myFunction не удаляет. Это и называется замыкание. tl;dr Замыкание - это когда невидимый объект, в котором функция хранит локальные переменные не удаляется после того как функция отработает. P.S. Кстати, этим "невидимым объектом" был Альберт Эйнштейн LexicalEnvironment.
  20. По умолчанию <div> имеет ширину 100%, задавая ему маргин, вы таким образом делаете его ширину меньше, на величину плавающего блока, что позволяет вашему .inside-wrapper обтекать float.
  21. 1) нода с текстом должна идти в коде после инпута 2) input:focus ~ .text { color: white; }
  22. getFunc() возвращает функцию (func), которая тут же вызывается. Вообще пример уродский, особенно для новичков.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy