Документация/Уроки по программированию/Создание нового игрового класса

From NeoAxis Engine Wiki

Jump to: navigation, search
Перейти на уровень выше

Contents

Введение

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

В качестве примера, мы создадим тип игрового объекта, который меняет свой материал при получении повреждения (например, при попадании пули).

Данный урок состоит из двух частей:

  • Создание класса игрового объекта,
  • Создание типа игрового объекта.

В первой части пойдет речь непосредственно о создании нового класса игрового объекта. Во второй - о создание типа (.type файл), основанном на новом классе.

Кстати, создаваемыей нами объект уже присутствует в SDK. Вы можете просто читать урок и не терять времени на написание кода с нуля.
Исходный код находится в файле ExampleMagicObject.cs проекта GameEntities.
Файлы описания типа расположены в папке Bin\Data\Types\Dynamic\ExampleMagicBall.

Создание класса игрового объекта

Создание классов

Приступим к созданию нового игрового класса. Запустим sln-файл из директории "Game\Src". Откроем проект GameEntities. Добавим к нему новый файл ExampleMagicObject.cs. Скопируем туда следующий код:

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Drawing.Design;
using Engine.MathEx;
using Engine.Renderer;
using Engine.MapSystem;
using Engine.PhysicsSystem;
 
namespace GameEntities
{
	public class ExampleMagicObjectType : DynamicType
	{
	}
 
	public class ExampleMagicObject : Dynamic
	{
		ExampleMagicObjectType _type = null; public new ExampleMagicObjectType Type { get { return _type; } }
	}
}

Как видим, в коде представлено два класса:

  • ExampleMagicObjectType - это класс, который будет отвечать за представление типа объекта в редакторе ресурсов. В этом классе обычно задаются свойства, которые будут настраиваться в редакторе игровых объектов.
  • ExampleMagicObject - это класс самого объекта, экземпляры которого будут созданы в игре. Один экземпляр, для каждого объекта на карте.

Наши классы, наследуются, соответственно, от классов DynamicType и Dynamic. Класс Dynamic реализует динамические объекты и лежит в основе многих других классов. Нам он подходит, т.к. у него есть метод OnDamage - реакция на повреждение объекта.

Обратим внимание на следующую строчку:

ExampleMagicObjectType _type = null; public new ExampleMagicObjectType Type { get { return _type; } }

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

Добавление свойств для типа

Добавим атрибут к нашему игровому типу, который можно будет редактировать в редактор игровых объектов.

public class ExampleMagicObjectType : DynamicType
{
	[FieldSerialize]
	string blinkMaterialName;
 
	/// <summary>
	/// Gets or sets the name of a replacing material.
	/// </summary>
	[Editor( typeof( EditorMaterialUITypeEditor ), typeof( UITypeEditor ) )]
	public string BlinkMaterialName
	{
		get { return blinkMaterialName; }
		set { blinkMaterialName = value; }
	}
}

Атрибут BlinkMaterialName содержит имя материала, на который будет заменяться материал объекта при выстреле.

Обратим внимание на следующую строчку.

[Editor( typeof( EditorMaterialUITypeEditor ), typeof( UITypeEditor ) )]

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

Переменная blinkMaterialName хранит имя материала, а [FieldSerialize] указывает на то, что этот атрибут будет записан в файл при сохранении игрового типа.

Написание логики класса

Теперь перейдем к классу ExampleMagicObject. Добавим к нему две переменные:

MeshObject blinkMeshObject;
string originalMaterialName;

blinkMeshObject будет хранить в себе трехмерную модель объекта, а originalMaterialName его исходный материал.

Следующий шаг. Добавим к нашему классу обработку метода OnPostCreate, вызываемый после создания объекта.

/// <summary>Overridden from <see cref="Engine.EntitySystem.Entity.OnPostCreate(Boolean)"/>.</summary>
protected override void OnPostCreate( bool loaded )
{
	base.OnPostCreate( loaded );
 
	//To find the first attached mesh
	foreach( MapObjectAttachedObject attachedObject in AttachedObjects )
	{
		MapObjectAttachedMesh attachedMesh = attachedObject as MapObjectAttachedMesh;
		if( attachedMesh != null )
		{
			blinkMeshObject = attachedMesh.MeshObject;
			break;
		}
	}
 
	//To save the original name of a material
	if( blinkMeshObject != null )
		originalMaterialName = blinkMeshObject.SubObjects[ 0 ].MaterialName;
}

Первая строчка вызывает родительский метод OnPostCreate:

base.OnPostCreate( loaded );

Затем среди прикрепленных объектов отыскивается меш и записывается в переменную blinkMeshObject:

foreach( MapObjectAttachedObject attachedObject in AttachedObjects )
{
	MapObjectAttachedMesh attachedMesh = attachedObject as MapObjectAttachedMesh;
	if( attachedMesh != null )
	{
		blinkMeshObject = attachedMesh.MeshObject;
		break;
	}
}

Наконец, в переменную originalMaterialName записывается имя исходного материала меша:

if( blinkMeshObject != null )
	originalMaterialName = blinkMeshObject.SubObjects[ 0 ].MaterialName;

Настало время написать функцию, которая бы меняла материал объекта при его повреждении. За повреждение объекта отвечает метод OnDamage. Добавим его к нашему классу:

//This method is called when the entity receives damages
protected override void OnDamage( MapObject prejudicial, Vec3 pos, Shape shape, float damage, bool allowMoveDamageToParent )
{
	base.OnDamage( prejudicial, pos, shape, damage, allowMoveDamageToParent );
 
	if( blinkMeshObject != null )
	{
		//To change a material
		if( blinkMeshObject.SubObjects[ 0 ].MaterialName == originalMaterialName )
			blinkMeshObject.SetMaterialNameForAllSubObjects( Type.BlinkMaterialName );
		else
			blinkMeshObject.SetMaterialNameForAllSubObjects( originalMaterialName );
	}
}

Здесь сначала вызывается родительский метод OnDamage:

base.OnDamage( prejudicial, pos, shape, damage, allowMoveDamageToParent );

Затем меняется материал объекта. Если стоит исходный материал, то меняется на blinkMaterialName. Если используется blinkMaterialName, то меняется на исходный:

if( blinkMeshObject.SubObjects[ 0 ].MaterialName == originalMaterialName )
	blinkMeshObject.SetMaterialNameForAllSubObjects( Type.BlinkMaterialName );
else
	blinkMeshObject.SetMaterialNameForAllSubObjects( originalMaterialName );

На этом написание кода для нашего объекта закончено.

Создание типа игрового объекта

Подготовка ресурсов

Материал для магического шара
Физическая модель для магического шара

Игровые типы создаются и настраиваются в редакторе игровых объектов (часть редактора ресурсов).

Запустим редактор ресурсов. Пусть наш тип располагается в директории "Data\Types\Dynamic\ExampleMagicBall".

Перед тем как создать тип, подготовим другие необходимые ресурсы. Нам понадобятся:

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

Кстати, данные ресурсы уже присутствуют в папке "Data\Types\Dynamic\ExampleMagicBall" как пример. Будет их использовать.

Создание типа

Теперь приступим к созданию типа игрового объекта. Нажмем правой кнопкой мыши на директорию ExampleMagicBall и в контекстном меню выберем пункт New Resource.

Magic ball3.jpg

В появившемся окне выберем тип ресурса: Entity Type. И нажмем кнопку Continue.

Magic ball4.jpg

Теперь укажем имя нового типа: ExampleMagicBall, класс: ExampleMagicObject, созданный нами. После чего нажмем кнопку Next.

Magic ball5.jpg

Редактирование типа

Дважды щелкнем на появившемся файле ExampleMagicBall.type, чтобы перейти в режим редактирования.

Теперь наша задача настроить модель (меш), физическую модель и указать "бликующий" материал (свойство BlinkMaterialName).

Начнем с настройки меша. Кликнем правой кнопкой по рабочему окну и в меню Attach Object выберем пункт Mesh.

Magic ball6.jpg

В качестве примера, выберем файл Ball.mesh в директории "Data\Types\Dynamic\Ball".

Magic ball7.jpg

Теперь настроим свойство BlinkMaterialName. Выделим игровой объект в списке Objects и нажмем кнопку "..." у параметра BlinkMaterialName.

Добавление бликующего материала к объекту

Выберем заранее подготовленный материал ExampleMagicBallBlinkMaterial.highMaterial из текущей директории.

Magic ball9.jpg

Теперь нужно присоединить к нашему типу физическую модель. Параметр PhysicsModel зададим нашей физической моделью - ExampleMagicBall.physics. Чтобы удостовериться, что физическая модель нам подходит, поставим флажок Show Physics в нижней панели редактора объектов. Радиус сферы меша и радиус сферы физической модели должны совпадать.

Магический шар и его физическая модель

Наконец, не забудем поставить свойство AllowEditorCreate в True, чтобы объект можно было создать в редакторе карт.

Теперь осталось сохранить созданный нами тип игрового объекта и проверить его работу.

Проверка работы

Настало время увидеть наш тип в действии. Добавим объект ExampleMagicBall на какую-либо карту. Объект в списке типов можно найти в директории "Types\Dynamic".

Магический шар на карте

Запустим режим симуляции. Возьмемся за Shotgun и протестируем магический шар. Вот так ExampleMagicBall выглядит перед выстрелом:

Магический шар перед выстрелом

А вот так сразу после выстрела:

Магический шар после выстрела

Итоги

Наш урок подошел к концу. Мы научились создавать классы игровых объектов и настраивать, основанные на них игровые типы.

Система объектов, включающая в себя классы и типы игровых объектов - это мощнейший инструмент для написания игровой логики в движке NeoAxis.