Phaser-сумісна гра з використанням MightyEditor

18 хв. читання

У цій статті я постараюся дати вам загальне уявлення про MightyEditor і про сам процес розробки. Туторіал покаже, як створити прототип міні-гри протягом години.

Вимоги

Нова версія браузера Google Chrome. Ви можете спробувати інші браузери, але на них тестування не проводилось.

Що таке MightyEditor?

MightyEditor - це open source рішення в хмарі для створення і хостингу HTML5-ігор. Він сумісний з Phaser - платформою для розробки ігор, але ви можете також використовувати його з іншими інструментами. Основними функціями редактора є: управління активами, редагування карт, редактор коду, експорт даних.

Як MightyEditor працює?

Процес проектування за допомогою редактора складається з наступних етапів:

  1. Створення проекту

  2. Завантажити активів

  3. Перенесення активів на мапу

  4. Об'єднання активів в шари, наприклад: фон, блоки...

  5. Додавання колізій і функціональності в редакторі коду

  6. Відкриття гри і експорт даних

Чому саме MightyEditor?

Наявність інструмента на основі браузера з усім базовим функціоналом редактора карт і коду дозволяє вам зосередитися на швидкому прототипуванні ігор. Відкрийте посилання в браузері і працюйте. Не потрібно витрачати час на встановлення та налаштування різних програмних рішень, тим самим ви заощаджуєте власний час.

Співпрацювати з дизайнером, іншим членом команди або клієнтом так же просто, як ділитися посиланням на проект. Ви дійсно можете працювати в команді, використовуючи Illustrator для всього, що стосується активів, в той час як гейм-дизайнер створює різні рівні в редакторі карт, а розробник додає функціональності, за допомогою JavaScript коду.

Ваш проект не залежить від MightyEditor. Всі активи та код можна експортувати в будь-який час. Так як редактор має відкритий вихідний код, ви можете премістити дані на комп'ютер і працювати з локальною версією редактора. Вихідний код можна знайти на GitHub.

Ідея міні-гри

У цьому уроці ми створимо просту міні-гру під назвою Digger. Гра про маленького шахтаря, який прибирає земельні блоки на шляху до золота і продає різні мінерали в магазині. Для навігації будуть використовуватися клавіші зі стрілками. До об'єкта будуть додані проста фізика і колізії.

Створення проекту

Відкрийте посилання <http: mightyeditor.mightyfingers.com="">. Введіть назву проекту - в нашому випадку - Digger.

Phaser-сумісна гра з використанням MightyEditor

На нижній правій панелі встановіть розміри гри: worldWidth, worldHeight. Для простоти ми залишимо розміри за замовчуванням - 640 x 640.

Phaser-сумісна гра з використанням MightyEditor

Завантаження активів

У наступній частині туторіалу ми будемо використовувати активи, які ви можете завантажити тут. На верхній панелі праворуч від списку вибору використовуйте upload file, щоб додати файли. Крім того, ви можете перетягувати файли на панель і вони будуть завантажені автоматично.

Phaser-сумісна гра з використанням MightyEditor

Створення карти

Натисніть кнопку на лівій панелі інструментів, а потім виберіть background_sky.png в панелі "активи" і натисніть на карту, таким чином, створюючи фон (утримуйте кнопку Ctrl - прив'язка до сітки). Ви можете змінити положення фону, вибравши стрілку ліворуч у панелі інструментів. Для більш точного позиціонування змініть x, y позиції в панелі налаштувань.

Повторіть цей крок з наступними активами: background_city.png, background_hill1.png, background_hill2.png, background_grass.png. В кінці все має виглядати, як це показано на наступному малюнку:

Phaser-сумісна гра з використанням MightyEditor

Створення групи

Давайте ініціалізуємо групу для новостворених об'єктів на задньому плані. На правій панелі Objects виберіть Add Group. Переіменуйте групу у bg і перетягніть об'єкти цієї групи. Для перетягування декількох об'єктів утримуйте натиснутою клавішу Shift.

Phaser-сумісна гра з використанням MightyEditor

Додавання іншої графіки

Давайте створимо 5 рядів блоків під фон. Є чотири різних типи: камінь, земля, трава, золото. Золоті блоки колекційні і Ви не можете копати каміння. Об'єкти повинні бути додані до групи blocks.

Phaser-сумісна гра з використанням MightyEditor

На останок, ми повинні додати персонажа і об'єкти крамниці. Актив для персонажа містить кілька кадрів. Потрібно визначити висоту та ширину кадру в нижній правій панелі Settings. Розмір: 90 х 90. Розділені кадри можна подивитися на нижній панелі assetPreview. Також, відредагуйте anchorX і anchorY. Встановіть значення 0,5 для обох параметрів.

Phaser-сумісна гра з використанням MightyEditor

Ви також можете визначити свої власні змінні для об'єкта. Відкрийте вкладку userData і встановіть параметр gold:

Phaser-сумісна гра з використанням MightyEditor

Виберіть значок текст на лівій панелі інструментів і помістіть об'єкт на верхньому правому кутку картки. Запис "0" в якості тексту буде повідомляти про кількість очок. Перейменуйте текстовий об'єкт на панелі об'єктів.

Phaser-сумісна гра з використанням MightyEditor

Тепер ми готові додати об'єкти і завершити редагування карти. Персонаж і об'єкти крамниці додаються, як на зображенні нижче:

Phaser-сумісна гра з використанням MightyEditor

Ми закінчили з графічною частиною. Тепер ми додамо функціональності: управління персонажем, колізії та ін.

Положення гри

Для кодування ми будемо використовувати Phaser development framework. Області Phaser - це окремі частини логіки гри. За замовчуванням редактор дає вам шаблон з чотирма областями: boot, load, menu, play. Для кожної області є деякі зумовлені методи: preload, create, update, render. Для демонстраційних цілей ви повинні знати, що метод create викликається відразу після завантаження всіх активів, метод update викликається циклом, 60 разів в секунду. Ви можете дізнатися більше в документації.

За замовчуванням шаблон викликає область menu. Для простоти ми не будемо створювати меню і відразу перейдемо до області play. Для цього треба викликати область play в menu.js файл, після методу create.

    window.Digger.state.menu = {
        create: function() {
            this.game.state.start("play");
        }
    }

Відкриття гри

Відкрийте гру за допомогою кнопки Open game. На даний момент є тільки чорний екран. Щоб відобразити об'єкти, ми повинні ініціалізувати їх у методі create файлу play.js.

    create: function() {
        this.bg = mt.create("bg");
        this.blocks = mt.create("blocks");
        this.shop = mt.create("shop");
        this.character = mt.create("character");
        this.points = mt.create("points");
    }

Ініціалізація виконується в функції mt.create. "bg" та "blocks" являють собою назви груп в об'єкт панелі. "shop" і "character" представляють спрайт імена в одноіменній панелі і, нарешті, "points" являє собою текст. Як ви бачите, всі об'єкти груп, спрайт і текст ініціалізуються одним і тим же методом.

Відкрийте гру і ініціалізовані об'єкти будуть видні на екрані.

Додавання фізики

За замовчуванням фізика в Phaser вимкнена для кращої продуктивності. Ми включимо найлегшу і найшвидшу фізику з усіх наявних - аркадну фізику. Відкрийте вкладку фізики в нижньому правому куті. Змініть параметр enable на 1, додаткові параметри відобразяться нижче. Встановіть розмір персонажа, ширина: 60 і висота: 60. Включіть гравітацію і встановіть значення y 1000\. Встановіть значення параметра collideWorldBounds 1.

Phaser-сумісна гра з використанням MightyEditor

Відкривши гру, ви побачите, що ваш персонаж провалюється до нижньої частини екрану.

Управління персонажем

Ініціалізуйте клавіші управління в методі create.

    this.cursors = this.game.input.keyboard.createCursorKeys();

Ця функція дає нам об'єкт з чотирма кнопками: вгору, вниз, вліво і вправо. В методі update ми будемо відстежувати, коли кнопка вліво/вправо/вгору натиснута, щоб рухати персонажа або зупиняти його.

    update: function() {
        if (this.cursors.left.isDown) {
            this.character.body.velocity.x = -200;
        } else if (this.cursors.right.isDown) {
            this.character.body.velocity.x = 200;
        } else {
            this.character.body.velocity.x = 0;
        } if (this.cursors.up.isDown) {
            this.character.body.velocity.y = -300;
        }
    }

Відкрийте гру і спробуйте покерувати персонажем за допомогою клавіш.

Зіткнення між персонажем і блоками

Включіть фізику для блоків і встановіть їх нерухомими у редакторі карт.

Phaser-сумісна гра з використанням MightyEditor

Далі додайте виявлення зіткнень в методі update. Перші два аргументи визначають, що ми будемо перевіряти зіткнення між спрайт об'єктом і групою об'єктів .Третій аргумент - двоаргументова функція яка викликається при зіткненні.

    this.game.physics.arcade.collide(this.character, this.blocks.self, function(character, block) {
        console.log('Collision between', character, block);
    }, null, this);

Відкривши гру, ви побачите, що персонаж не впаде до кінця екрану, але триматиметься на верхній частині блоків.

Анімування персонажа

Спочатку ми повинні визначити кадри в spritesheet для різних типів анімації. Додайте наступні рядки в метод create.

    this.character.animations.add('stand', [0, 1, 2, 3], 10, true);
    this.character.animations.add('fly', [4, 5, 6, 7], 10, true);
    this.character.animations.add('run', [8, 9, 10], 10, true);
    this.character.animations.add('fall', [12, 13, 14, 15], 10, true);
    this.character.animations.add('dig', [16, 17, 18], 10, false);
    this.character.animations.add('dig_down', [20, 21, 22], 10, false);
    this.character.animations.play('stand');

Останній рядок коду починає відтворення анімації за замовчуванням - стояти.

Для іншої анімації, ми повинні змінити спосіб оновлення і додати конкретні анімації для кожної клавіші. Зверніть увагу, що у нас однакові анімації для лівої і правої кнопки. Для того, щоб обернути спрайт ми використовуємо значення -1.

    if (this.cursors.left.isDown) {
        this.character.body.velocity.x = -200;
        this.character.animations.play('run');
        this.character.scale.x = -1;
    } else if (this.cursors.right.isDown) {
        this.character.body.velocity.x = 200;
        this.character.animations.play('run');
        this.character.scale.x = 1;
    } else {
        this.character.body.velocity.x = 0;
        this.character.animations.play('stand');
    } if (this.cursors.up.isDown) {
        this.character.body.velocity.y = -300;
        this.character.animations.play('fly');
    }

Знищення блоків

Для того, щоб копати блоки потрібно додати функціональність до нашої функції:

    this.game.physics.arcade.collide(this.character, this.blocks.self, function(character, block) {
        if (this.cursors.left.isDown) {
            if (block.body.touching.right) {
                this.destroyBlock(block);
            }
        }
        if (this.cursors.right.isDown) {
            if (block.body.touching.left) {
                this.destroyBlock(block);
            }
        }
        if (this.cursors.down.isDown) {
            if (block.body.touching.up) {
                this.destroyBlock(block);
            }
        }
    }, null, this);

Різні блоки повинні оброблятися по-різному. Трави і грунтові блоки просто знищуються, скелі неможливо зруйнувати. Також, нам потрібно збирати золото і змінювати значення параметра. Додайте destroyBlock метод до області play.

    destroyBlock: function(block) {
        switch (block.key) {
            case '/rock.png':
                break;
            case '/grass.png':
            case '/ground.png':
                block.destroy();
                break;
            case '/gold.png':
                this.character.getData().userData.gold++;
                block.destroy();
                break;
        }
    }

Продаж золота

В наступному кроці ми повинні продавати зібране золото в магазині. В кінці методу update додайте наступні рядки:

    if (this.checkOverlap(this.character, this.shop)) {
        if (this.character.getData().userData.gold > 0) {
            var newPoints = parseInt(this.points._text) + this.character.getData().userData.gold;
            this.points.setText(newPoints);
            this.character.getData().userData.gold = 0;
        }
    }

І створіть новий метод , який перевіряє характер і межі спрайту:

    checkOverlap: function(spriteA, spriteB) {
        var boundsA = spriteA.getBounds();
        var boundsB = spriteB.getBounds();
        return Phaser.Rectangle.intersects(boundsA, boundsB);
    }

Рефакторинг і додавання фінальної анімації

Ми додамо анімацію копання як заключний крок цього уроку і змінимо метод update, щоб задовольняти потреби гри. Наведений нижче код повинен бути зрозумілий усім.

    "use strict";
    window.Digger.state.play = {	
    	create: function(){
    		this.cursors = this.game.input.keyboard.createCursorKeys();
    		
    		this.bg = mt.create("bg");
    		this.blocks = mt.create("blocks");
    		this.shop = mt.create("shop");
    		this.character = mt.create("character");
    		this.points = mt.create("points");
    		
    		this.character.animations.add('stand', [0, 1, 2, 3], 10, true);
    		this.character.animations.add('fly', [4, 5, 6, 7], 10, true);
    		this.character.animations.add('run', [8, 9, 10], 10, true);
    		this.character.animations.add('fall', [12, 13, 14, 15], 10, true);
    		this.character.animations.add('dig', [16, 17, 18], 10, false);
    		this.character.animations.add('dig_down', [20, 21, 22], 10, false);
    		this.character.animations.play('stand');
    	},
    	
    	update: function(){
    		var collideDown = false;
    		this.game.physics.arcade.collide(this.character, this.blocks.self,
    		  function(character, block){
    			
    			if(this.dig) return;	
    				
    			if(this.cursors.left.isDown){		
    				if(block.body.touching.right){
    					this.dig = this.character.animations.play('dig');
    					this.dig.onComplete.addOnce(function(){
          					this.destroyBlock(block);
    					}, this);							
    				} else {
    					this.character.animations.play('run');
    				}					
    			}
    
    			if(this.cursors.right.isDown){					
    				if(block.body.touching.left){
    					this.dig = this.character.animations.play('dig');
    					this.dig.onComplete.addOnce(function(){
          					this.destroyBlock(block);
    					}, this);					
    				} else {
    					this.character.animations.play('run');
    				}
    
    			}
    
    			if(this.cursors.down.isDown){					
    				if(block.body.touching.up){
    					this.dig = this.character.animations.play('dig_down');
    					this.dig.onComplete.addOnce(function(){
          					this.destroyBlock(block);
    					}, this);
    
    				} else {
    					this.character.animations.play('stand');
    				}
    			}		
    			
    			if(block.body.touching.up){
    				collideDown = true;
    			}
    				
    			
    			
    		}, null, this);
    										 
    		
    		if(this.dig){
    			return;	
    		}
    		
    
    		if(this.cursors.left.isDown){
    			this.character.scale.x = -1;
    			this.character.body.velocity.x = -200;
    		}
    		else if(this.cursors.right.isDown){
    			this.character.scale.x = 1;
    			this.character.body.velocity.x = 200;
    		}
    		else {
    			this.character.body.velocity.x = 0;	
    		}
    		
    		
    		if(this.cursors.up.isDown){
    			this.character.body.velocity.y = -300;
    			this.character.animations.play('fly');
    		} else {
    			if(!collideDown){
    				this.character.animations.play('fall');
    			}
    			else if(this.character.body.velocity.x === 0){
    				this.character.animations.play('stand');
    			}
    		}
    		
    		if(this.checkOverlap(this.character, this.shop)){
    			if(this.character.getData().userData.gold > 0){
    				var newPoints = parseInt(this.points._text) + this.character.getData().userData.gold;
    				this.points.setText(newPoints);
    				this.character.getData().userData.gold = 0;
    			}
    		}
    	},
    	
    	destroyBlock: function(block){
    		this.dig = false;
    		switch(block.key){
    			case '/rock.png':
    				break;
    			case '/grass.png':
    			case '/ground.png':
    				block.destroy();
    				break;
    			case '/gold.png':
    				this.character.getData().userData.gold++;
    				block.destroy();
    				break;
    		}
    	},
    	
    	checkOverlap: function (spriteA, spriteB) {
        	var boundsA = spriteA.getBounds();
        	var boundsB = spriteB.getBounds();
        	return Phaser.Rectangle.intersects(boundsA, boundsB);
    	},
    	
    	stopDig: function(){
    		this.dig = false;	
    	}
    };

Проект гри тут.

Фінальна гра тут.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.2K
Приєднався: 7 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація