Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

2D-графіка в XNA Game Studio 2.0 / 3.0


У цій лабораторній роботі ми розглянемо систему координат, яка використовується при створенні двовимірних ігор, поговоримо про виведення двовимірних зображень в XNA, розглянемо питання накладення зображень, налаштування розмірів ігрового вікна і роботи у віконному і повноекранному режимах.







Система координат


У розробці двовимірних ігор використовується двовимірна система координат. На рис. 2.1. ви можете бачити звичайну систему координат і об'єкт, зображений в цій системі.

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.1. Традиційна система координат

У даній системі координат початок координат розташовано в лівому нижньому кутку системи - саме тут знаходиться точка 0,0. Координати по осі Х зростають зліва направо, по осі Y - від низу до верху.

При розробці комп'ютерних ігор використовується екранна система координат. Головна відмінність цієї системи від традиційної полягає в тому, що початок координат розташовано в лівому верхньому кутку екрану. Координати по осі Х зростають зліва направо, а ось вісь Y виявляється перевернутої - її координати зростають зверху вниз. На рис. 2.2. ви можете бачити цю систему координат, в якій намальований той же трикутник.

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.2. Екранна система координат

Межі екранної системи координат - це або кордону ігровий області вікна - при віконному режимі роботи, або - дозвіл екрана при екранному режимі. Наприклад, якщо в віконному режимі ми маємо розмір вікна, рівним 800х600 пікселів, то саме ці числа є межами системи координат.

Створимо новий стандартний ігровий проект з ім'ям P2_1. В панелі Solution Explorer клацнемо правою кнопкою миші по папці Content і в контекстному меню виберемо пункт Add (Existing Item. З'явиться стандартне вікно для роботи з файлами. Знайдемо з його допомогою графічний файл і додамо в проект. В результаті панель Solution Explorer повинна виглядати так , як зображено на рис. 2.4.

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.4. Зображення, доданий в проект

Лістинг 2.1. Змінні для роботи з зображенням


Метод Begin () об'єкта spriteBatch готує графічний пристрій до висновку зображення. Метод Draw того ж об'єкта приймає в якості параметрів змінну типу Texture2D (MySprite), змінну типу Vector2 (position) і колір, відповідно до якого буде змінений відтінок зображення. В даному випадку це білий колір - це означає, що кольори зображення залишаться незмінними. Метод End () завершує процедуру виведення - зображення виводяться на екран.

Метод Draw базового класу в даному випадку не використовується для виведення зображень. Ми розглянемо його роль у виведенні зображень нижче.

Запустивши програму, ми отримаємо таке зображення (рис. 2.5.):

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.5. Зображення, виведене на ігровий екран

Розглянемо підхід, який дозволяє спростити підхід до висновку зображень методом, описаним вище.

Розробка класу для зберігання графічної інформації


Створимо новий стандартний проект з ім'ям P2_2. Додамо в проект зображення, яке ми створили вище (P2_1.png).

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

Клацнемо по значку проекту P2_2 в панелі Solution Explorer і виберемо в меню пункт Add (Class. З'явиться вікно додавання нового елемента, в якому буде виділено шаблон порожнього класу (рис. 2.6.).

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.6. Додаємо в проект новий клас

Задамо ім'я нового класу - нехай це буде spriteClass, і натиснемо кнопку Add. У проект буде додано новий порожній клас. Ось як виглядає наш проект після всіх перерахованих дій (рис. 2.7.).

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.7. Зовнішній вигляд вікна проекту після додавання нового класу

Модифікуємо код класу spriteClass наступним чином.

По-друге - створимо необхідні властивості класу.

По-третє - побудуємо конструктор класу, який буде ініціалізувати ці властивості.

Ось як виглядає код класу після всіх модифікацій (лістинг 2.4.):

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

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2







Мал. 2.8. Створення зображень в Adobe Photoshop

Тут ми створили три зображення - ці зображення на наступних заняттях знадобляться нам для створення власного варіанту класичної гри Pong.

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.9. Визначаємо координати спрайту в графічному файлі

Точно так же дізнаємося координату правого нижнього кута зображення. У нашому випадку це - (35, 97). Для подальшої роботи нам знадобиться знання координати лівої верхньої точки прямокутника, що обмежує спрайт, а так само - його ширина і висота. У нашому випадку ширина становить 17 пікселів (35- 18), висота - 88 пікселів (97 - 9). У підсумку отримуємо наступні параметри прямокутника, в який укладено наш спрайт:

  • Х: 18
  • Y: 9
  • Width: 17
  • Height: 88

Нижче ці дані знадобляться нам.

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

Лістинг 2.8. Доопрацьований код ігрового компонента


Розглянемо зміни, внесені в компонент.

Ми додали три змінних - sprTexture, sprRectangle, sprPosition - для зберігання текстури, прямокутника, що показує координати спрайту в текстурі, і позиції виведення спрайту на екран.

Далі, ми модифікували конструктор класу, додавши до списку переданих параметрів наступні змінні:

  • ref Texture2D newTexture - передача текстури. Параметр передається по посиланню, тобто об'єкт одержує не копію текстури, а лише посилання на неї - це економить системні ресурси.
  • Rectangle newRectangle - прямокутник, що задає координати спрайту в зображенні.
  • Vector2 newPosition - позиція спрайту на ігровому екрані.

У тілі конструктора ми инициализируем відповідні властивості класу переданими параметрами.

Далі, ми допрацьовуємо код методу Draw. Тут нам належить найбільш складна частина модифікації. Справа в тому, в грі, для якої ми створюємо компонент, вже є всі засоби для виведення зображень. Тому найрозумніше скористатися вже існуючим об'єктом типу SpriteBatch для того, щоб з його допомогою вивести зображення на екран. Для того, щоб зробити один раз створений SpriteBatch доступним для всіх компонентів гри, існує спеціальна методика. Є таке поняття, як ігрові сервіси, які доступні всім компонентам. Спочатку ми додамо створений в класі Game1 об'єкт типу SpriteBatch в список ігрових сервісів (ми розглянемо це нижче), після чого зможемо витягти цей об'єкт в ігровому компоненті. Ми так і робимо - наступна команда створює новий об'єкт sprBatch, який містить посилання на вихідний об'єкт типу SpriteBatch, який раніше, в класі Game1, доданий в список ігрових ресурсів.

SpriteBatch sprBatch = (SpriteBatch) Game.Services.GetService (typeof (SpriteBatch));

sprBatch.Draw (sprTexture, sprPosition, sprRectangle, Color.White);

Вище ми виводили спрайт на екран, спочатку викликавши метод Begin () об'єкта типу SpriteBatch, потім - викликали метод Draw (), потім - метод End (), завершальний висновок. Тут ми не викликаємо цих методів. Справа в тому, що дані методи будуть викликані в методі Draw (), який викликає основна гра (об'єкт типу Game1). У код класу Game1 внесені деякі зміни, які ми зараз розглянемо. У лістингу 2.9. ви можете бачити цей код.

Лістинг 2.9. Код класу Game1

Для початку ми додали нові змінні, одна з яких (типу spriteComp) служить для зберігання об'єкта класу spriteComp, а друга (типу Texture2D) служить для зберігання текстури. Так як текстура містить кілька спрайтів, змінна Texture2D може знадобитися при створенні декількох об'єктів, тому має сенс створити цю змінну, один раз завантажити в неї зображення і користуватися їй.

У методі LoadContent () ми, як завжди, створюємо об'єкт типу SpriteBatch, і, за допомогою команди Services.AddService (typeof (SpriteBatch), spriteBatch); додаємо об'єкт SpriteBatch до списку ігрових сервісів. Саме після цієї команди spriteBatch можна викликати в ігрових компонентах і виводити їх зображень.

Далі в цьому методі ми завантажуємо текстуру з зображеннями в змінну texture і викликаємо метод CreateNewObject () ;. Цей метод ми створили самостійно - ми використовуємо його для створення об'єкта gameObject.

gameObject = new spriteComp (this, ref texture, new Rectangle (18,9,17,88), new Vector2 (100,150));

При створенні цього об'єкта ми передаємо конструктору класу spriteComp об'єкт гри (this), посилання на текстуру, об'єкт Rectangle - зверніть увагу на те, що ми створюємо новий об'єкт Rectangle, який инициализирован значеннями координати потрібного нам спрайту в файлі текстури, обчисленими раніше. Так само ми передаємо конструктору новий об'єкт типу Vector2, який містить координати спрайту для виведення на ігровий екран.

У методі CreateNewObject () ми розмістили ще одну команду - Components.Add (gameObject) ;. З її допомогою ми додаємо в список компонентів гри щойно створений об'єкт. Це дуже важлива команда - завдяки їй при виконанні команди base.Draw (gameTime); буде оброблений метод Draw нашого ігрового компонента, і зображення, які він виведе, будуть виведені на екран.

У методі Draw () класу Game1 ми використовуємо такий код:

Команда spriteBatch.Begin (); готує графічне устройтство для виведення зображень, після цього здійснюється висновок зображень - за викликом base.Draw (gameTime); обробляються відповідні методи ігрових компонентів, успадкованих від DrawableGameComponent. Нагадаю, що в методі Draw () нашого компонента spriteComp є лише виклик методу Draw () об'єкта класу SpriteBatch, без викликів Begin () і End (). В результаті, після того, як компонент виведе зображення, висновок завершується командою End () об'єкта spriteBatch в методі Draw () класу Game1 і зображення з'являється на екрані.

Такий механізм роботи дозволяє створювати безліч компонентів, додавати їх в список компонентів гри і не піклуватися про виведення кожного з них в методі Draw () класу Game1. Після того, як компонент доданий в список компонентів гри, висновок його графічного зображення здійснюється автоматично. Можна сказати, що метод base.Draw (gameTime); «Переглядає» всі зареєстровані компоненти і виводить їх на екран.

Саме такий підхід найзручніше використовувати при розробці реальних ігор.

Ось як виглядає ігровий екран після виведення в нього зображення (рис.2.10.).

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.10. Ігровий екран після виводу компонента

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

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

Наприклад, розглянемо висновок фонового зображення в уже створеному проекті P2_3. Вище ми створили зображення, які можна використовувати для створення власної версії гри Pong. Точно так же створимо зображення, яке буде служити фоном для гри. Стандартний ігровий екран має роздільну здатність 800х600 пікселів. Створимо зображення такого розміру (назвемо його background.png), завантажимо його в проект. Для цього додамо в клас нову змінну

Після цього, в методі LoadContent (), завантажимо зображення в цю змінну командою

Тепер виведемо зображення за допомогою методу Draw () об'єкта spriteBatch.

spriteBatch.Draw (backTexture, new Rectangle (0, 0, 800, 600), Color.White);

Помістимо цю команду між викликами Begin () і End () перед командою base.Draw (gameTime);
В результаті вікно гри набуде такого вигляду (рис. 2.11.).

Xna новини уроки статті () - статті 2d-графіка в xna game studio 2

Мал. 2.11. Вікно виведення гри після додавання фону

Як бачите, спрайт ігрового об'єкта виведено поверх фонового зображення.

ПРИМІТКА
  • Підхід, який вимагає дотримання черговості виведення зображень для регулювання їх накладення, не дуже зручний. Однак, XNA Framework передбачає спеціальні інструменти для настройки взаємного розташування об'єктів. Зокрема, при виведенні спрайту можна вказувати «глибину» (depth), на якій він розташований.

питання