[Лог #7] Класс рисовальщика.

Добавлен , опубликован

Рендер

Темой этого лога станет рендер. Расскажу немного о том как я себе представляю виртуального художника и почему лучше реализовать интерфейс IRender и класс-реализацию CRenderOGL, наример.
Хочется провести некоторые аналогии, которые по моему очень чётко вписываются.
Класс "рисовальщик" именовал я раньше как RenderDevice, но что это такое вообще? Зачем какие-то классы городить, когда есть, например, OpenGL? Конечно для удобства использования.
Класс IRenderDevice можно назвать художником, причём очень хорошо ему это имя подходит.
Можно сказать художнику "нарисуй мне вот тут квадрат", собственно это было бы эквивалентно такому вот вызову в коде "pRenderDevice->DrawQuad( .... )".
Художник не может нарисовать ничего, если у него нет холста, красок и кистей, и тут вновь можно провести аналогию.
Роль холста будет играть "контекст устройства"( как я понимаю это просто особая видеопамять, вернее её часть) - имеется у всех окон. Тут всё ясно холст - окно.
Кисти или их эквивалент - модели и геометрия, даже не имеет значения 2Д или 3Д. Возможно, кто-то знает, что геометрия(статичная?) в играх на движке Source, idTech и UnrealEngine называется Brush ( кисть ) - наверно просто совпадение, не думаю.
Кисть по холсту художник проведёт, но ничего не нарисует, поэтому нужны краски - текстуры, материалы, шейдеры (shaders). Думаю многие знают что из себя представляет шейдер - это подпрограмма для видеокарты, которая говорит как будет рисоваться 3Д модель. Экранный пост-процессы \ пост-эффекты - это тоже шейдер, например, свечение (glow, bloom), переход картинки в серость\цвет ( такой эффект был в игре Saboteur ).
Аналогии очень хорошо описывают набор классов графической библиотеки, однако, надо заметить, что нет конкретики. Это некая абстракция.
IRenderDevice - художник, а CRenderDeviceOGL - какой-нибудь Вася Пупкин, который вообще то тоже художник, но как видно уже конкретный.
Примерно вот так может выглядеть класс, вернее интерфейс для "художника":
class IRenderDevice
{
public:
	// Первичная инициализация GAPI-устройства
	virtual TResult	Init( WindowT *Window, int param ) = 0;

	// Инициализировать графический контекст для конкретного окна приложения 
	virtual TResult 	InitContextForWindow( WindowT *Window ) = 0;

	// Освободить граф.контекст окна
	virtual TResult 	CleanupContextForWindow( WindowT *Window ) = 0;

	// Сделать текущим(главным) контекст окна
	virtual TResult 	MakeCurrent( WindowT *Window ) = 0;

	// Сменить буфер изображения окна
	virtual TResult 	SwapBuffer( WindowT *Window ) = 0;

	// Финальная очистка граф.устройства
	virtual TResult 	Cleanup() = 0;

	virtual void ApplyView( GView * View ) = 0;
        virtual void SetMaterial( GMaterial * material ) = 0;
	virtual void DrawMesh( GMesh * Mesh ) = 0;
};
У методов этого класса нет реализации (конкретного кода) и они все виртуальные. Видно, что несколько первых методов - это инициализация, обновление кадра и освобождение ресурсов ( Init, SwapBuffer и Cleanup соответственно) - эти функции платформозависимые, что не есть хорошо вообще то. Но можно чуть-чуть избавится от зависимости "спрятав" зависимые от платформы типы и функции, например, тип переменной WindowT - произвольный (определённый мной, а не платформой), внутри которого есть платформозависимые переменные. Это сделано для того, чтобы не менять вызов функции, например:
Этот код не нужно будет менять для каждой платформы
pRenderDevice->Init( pMainWindow, 0 );
Для каждой ОС придётся лишь менять\написать код для класса WindowT и некоторые части от RenderDevice ( и\или других систем ).
Насколько это правильное решение - не знаю точно, возможно это вообще плохое решение.
Последние три функции интерфейса IRenderDevice - "применить вид", "установить материал" и "нарисовать модель". Конечно это не полный список функций, который пригодится, но для очень простых игр думаю даже такого хватит.
Функция ApplyView, которая принимает указатель на некий объект класса GView - особая штука, которой я можно сказать горжусь (хотя на самом деле нечем). GView - это что-то вроде виртуальной камеры, границ вида (вьюпорт \ viewport ) и матрицы проекции. Хранит в себе две матрицы - проекции (перспективная\ортогональная) и вида (положение и направление камеры).
Классы материала и сетки(mesh) вообще могут быть какими угодно, вернее информация которая в них хранится может быть различной и зависеть от "старшей" графической библиотеки (OpenGL \ DirectX).
Но не только материал и 3Д модель зависит от выбора библиотеки, собственно вся реализация интерфейса зависит от неё. Вот например если взять OpenGL, то может быть что-то вроде этого:
class GRenderDevice : public IRenderDevice
{
public:
	GRenderDevice();
	~GRenderDevice();

	// Первичная инициализация GAPI-устройства
	TResult		Init( WindowT *Window, int param );
	// Инициализировать графический контекст для конкретного окна приложения 
	TResult 	InitContextForWindow( WindowT *Window );
	// Освободить граф.контекст окна
	TResult 	CleanupContextForWindow( WindowT *Window );
	// Сделать текущим(главным) контекст окна
	TResult 	MakeCurrent( WindowT *Window );
	// Сменить буфер изображения окна
	TResult 	SwapBuffer( WindowT *Window );
	// Финальная очистка граф.устройства
	TResult 	Cleanup();

	void ApplyView( GView * View );
	void DrawMesh( GMesh * Mesh );

	void DrawVText( float x, float y, const char *strText, float size, bool bReversY = false );

	// Flags = 1 - color, 2 - depth, 4 - stencil
	enum{
		BIT_COLOR = 0x1,
		BIT_DEPTH = 0x2,
		BIT_STENCIL=0x4
	};
	void ClearScreen( int Flags = 0 );
	void ClearScreenColor( const float r, const float g, const float b, const float a );

	void BindTexture( GTexture * Texture );
	void SetWorldMatrix( const float * matrix );
	void SetWireframe( bool show = true );

	void	DrawRect( float x, float y, float xx, float yy );

protected:
	TResult		InitExtensions();
	HGLRC		m_hContext;      // На самом деле зависимая от платформы переменная
	WindowT		*m_pOwnerWindow;
};
Видно, что функций больше, да и переменные появились. (не забывайте, что это примеры, а в реальном проекте будет иначе). Можно создавать экземпляр этого класса и пользоваться всеми этими функциями, однако, если нужна независимость от OpenGL\DirectX, то нужно использовать IRenderDevice у которого функций меньше. Зачем тогда использовать этот урезанный класс? Дело в том, что реализацию художника можно загружать из DLL (если это реализовано, конечно), например, так сделано в Quake2 - рендер и механика игры загружаются из DLL.
Ну вот скажем та же функция ApplyView для OpenGL реализации будет выглядеть примерно так:
void GRenderDevice::ApplyView( GView * View )
{
	if( !View ) return;
// Задать вьюпорт
	View->SetViewport( 0, 0, GetBufferWidth(), GetBufferHeight() );

	glViewport( View->GetX(), View->GetY(), View->GetWidth(), View->GetHeight() );

// Утсановить матрицы (OpenGL-специичные функции)
	glMatrixMode( GL_PROJECTION );
	glLoadMatrixd( &View->GetProjectionMatrix().x[0][0] );
	glMatrixMode( GL_MODELVIEW );
	glLoadMatrixd( &(View->GetViewMatrix()).x[0][0] );
}
Или вот функция рисования сетки:
void GRenderDevice::DrawMesh( GMesh * Mesh )
{
	if( !Mesh ) return;

	GVertex3D * V		= Mesh->GetVertexArray();
	unsigned int Count	= Mesh->GetCountVertex();

// вновь специфичные функции
	glVertexPointer(	3,	GL_FLOAT, sizeof(GVertex3D), &(V[0].x) );
	glNormalPointer(		GL_FLOAT, sizeof(GVertex3D), &(V[0].nx) );
	glTexCoordPointer(	2,	GL_FLOAT, sizeof(GVertex3D), &(V[0].tu) );

// получить количество под-сеток
	unsigned int count_batch = Mesh->GetCountBatches();

// для каждой под-сетки ...
	for( unsigned int i=0; i< count_batch; ++i )
	{
		GBatch *b = 0;
		if( b = Mesh->GetBatch(i) )
		{

			if( b->m_MaterialID != -1 )
			{
//                             установить текстуру 
				BindTexture( FindTextureByID( b->m_MaterialID ) );
			}

//                     Нарисовать треугольники 
			glDrawElements( GL_TRIANGLES, b->GetCount(), GL_UNSIGNED_INT, b->GetPtr() );
		}
	}
}
Для DirectX реализации были бы совсем другие функции, но смыл бы не изменился.
Поэтому вызывая pRenderDevice->ApplyView( pView ) будет срабатывать специфичный код.
Честно говоря не очень хорошо получается у меня объяснить весь смысл, да что там - вообще не понятно что я тут написал :D
Конечно хотелось бы больше конкретного кода, но не учитель я :(
Да ещё и версия OpenGL, которую я ранее и использовал устарела, теперь придётся чуть обновить свои знания и подтянуть версию хотя бы OpenGL 3.3.

Что дальше ?

Думаю либо чуть увеличить разрыв между логами, либо небольшую паузу взять. Думаю начать уже конкретно проектировать само приложение (не писать код) и сделать небольшой альфа-прототип на каком-нибудь Unity (хотел вообще на GameMaker, но похоже с новым обновлением они отказались от WinXP - Steam отказывается запускать GM).
Заметьте ни одной картинки D: !
`
ОЖИДАНИЕ РЕКЛАМЫ...
1
14
9 лет назад
Отредактирован Kozinaka
1
У меня, если по-грубому, то так:
class Car : Object
{
    public Car(IGameEngineForObjects engine);

    public bool ProcessIteration(float dt);
    
    public bool Exploide();
    public bool Move();

    private IGameEngineForObjects _engine;
}

bool Car::ProcessIteration(dt)
{
    if(_gameEngine->GetCollisions(this) != null)
    {        
        return Exploide();
    }

    return Move(dt);
}
Для бочки аналогично. Объект оперируя интерфейсом движка, в котором есть только те методы, которые могут понадобиться объектам, запрашивает у движка список объектов, с которыми он сталкивается. Дальше либо объекты друг с другом специфически взаимодействуют.
0
29
9 лет назад
0
DarkDes, объекты не должны взаимодействовать напрямую. Берем пример моей архитектуры, добавляем в контроллер пару событий. OnCollision - столковение, OnDamage - нанесение урона. Для боычки при столкновении проверяем силу удара, отбираем игровые объекты, котороые расположены в определнном радиусе, расчитываем базовый урон, который можно было бы нанести этому объекту, затем вызываем OnDamage. Дальше у машины обрабатываем это событие, расчитываем сопротивление, уменьшая урон и дальше уже уменьшаем ХП
0
15
9 лет назад
0
_gameEngine->GetCollisions(this)
OnCollision
Вот как раз об этом. Коллизия подразумевается "прямая" физическая или же триггеры тоже работают ? Я это к тому, что объект "бочка" может быть заминирована и взрываться при попадании объекта в некий радиус\объём.
И попадание пули в бочку похоже решается каким-нибудь _gameEngine->RayTrace ? но для трассировки нужны ведь точка (пушка машины) и направление (камера? направление машины?).
0
29
9 лет назад
0
Вот как раз об этом. Коллизия подразумевается "прямая" физическая или же триггеры тоже работают ?
Это как реализуешь
DarkDes:
И попадание пули в бочку похоже решается каким-нибудь _gameEngine->RayTrace ? но для трассировки нужны ведь точка (пушка машины) и направление (камера? направление машины?).
Так ты трассировку то как делать будешь? Внешним объектом? Трассировкой должна и заниматься сама пушка, а у нее уже есть эти данные
3
14
9 лет назад
Отредактирован Kozinaka
3
Это как реализуешь
Присоединяюсь.
Типовые взаимодействия типа столкновений, срабатываний на расстоянии, трассировки по лучу и прочего должны быть реализованы в движке, чтобы такое взаимодействие в игре везде работало одинаково и реализовывалось в одном месте: GetCollisions(obj), GetObjectsInRadius(obj, radius), GetObjectsOnRay(obj, ray_direction, ray_distance) и т.д.
А вот специфические взаимодействия реализуются внутри игровых объектов, например у меня червь может пугать мелкую живность - это реализовано публичным методом Scare() у класса Creature. Червь берёт у игрового движка список объектов в некотором радиусе и каждого "пугает" запуском obj->Scare(this), дальше существо в реализации метода само решает, испугаться ему или нет. Или, например, EMP-пушка обездвиживает на некоторое время механизмы - проще у абстрактного класса MechanicalUnit реализовать метод Paralize(time) и дёргать его напрямую, чем проталкивать OnParalaze() от корневого Object'а - живым юнитам от этого мусора никакой пользы. Если конечно нет парализующих ядов - тогда парализация становится типовым взаимодействием. По здравому смыслу, короче.
1
24
9 лет назад
Отредактирован prog
1
А вот специфические взаимодействия реализуются внутри игровых объектов, например у меня червь может пугать мелкую живность - это реализовано публичным методом Scare() у класса Creature. Червь берёт у игрового движка список объектов в некотором радиусе и каждого "пугает" запуском obj->Scare(this), дальше существо в реализации метода само решает, испугаться ему или нет. Или, например, EMP-пушка обездвиживает на некоторое время механизмы - проще у абстрактного класса MechanicalUnit реализовать метод Paralize(time) и дёргать его напрямую, чем проталкивать OnParalaze() от корневого Object'а - живым юнитам от этого мусора никакой пользы. Если конечно нет парализующих ядов - тогда парализация становится типовым взаимодействием. По здравому смыслу, короче.
А вот тут я сторонник более сложного в реализации, но более гибкого подхода. А именно - взаимодействия делать не напрямую, а через цепочку эффектов. Объясню на твоем-же примере с червями:
  • червь время от времени генерирует эффект "ScareThemAll", относящегося к семейству действующих по области эффектов
  • эффект "ScareThemAll" берет список объектов в радиусе и к каждому применяет эффект "ScareTarget"
  • эффект "ScareTarget" умеет только вызывать метод Scare для цели, что он и делает.
  • у эффекта есть поле "caster", в котором по всей цепочке объектов передается ссылка на первоначальный источник эффекта, в данном случае на червя, что позволяет в эффекте "ScareTarget" сделать obj->Scare(caster)
Причем "ScareThemAll" вполне может быть реализован не отдельным классом, а экземпляром класса AreaEffect с параметрами, в то время как для "ScareTarget" в текущей архитектуре нужен отдельный класс, наследуемый от "TargetEffect".
P.S. все имена и ситуации вымышлены, все совпадения с реальностью абсолютно случайны.
0
29
9 лет назад
0
prog, если не ошибаюсь, то это что-то типа MessageQueue?
0
24
9 лет назад
0
alexprey, не совсем так - элементы иерархии эффектов можно и напрямую дергать, а можно и в очереди ложить и что угодно с ними делать, речь именно о вынесении реализации взаимодействий в отдельную иерархию, а реализация взаимодействия между иерархиями это уже отдельный вопрос.
0
29
9 лет назад
0
prog, ну в принципе твоя идея тоже очень хорошая
0
24
9 лет назад
0
alexprey, похожим подходом пользуются, например, близзард во втором старкрафте, правда на еще более высоком уровне абстракции.
0
14
9 лет назад
0
prog, ага, хорошая тема! Пока у меня мало типов взаимодействий, справляюсь напрямую, но в будущем скорее всего что-нибудь такое понадобится запилить.
0
15
9 лет назад
0
[Слоупок мод] Можешь примеры этих всяких баффов показать? Или статью знаешь какую? Интересовался как-то этой темой для другого "проекта", но ничего лучше этого (см.рис. часть с эффектами) не придумал :(
Интересно вообще создание скиллов абсолютно разных.
Загруженные файлы
3
29
9 лет назад
3
Теории это хорошо, только когда на большом проекте поймете, что уже слишком много AbstractSingletonProxyFactoryBean и ваще хоть и офигенно гибко, но вдвойне неудобно кодить, т.к. сущностей миллион и их надо постоянно создавать, связывать и прочее, поймети что на практике оно в основном неоч выходит! Проще надо быть, я щитаю. Тот же MVC не везде применим с точки зрения удобства кодинга и везде его пихать - скорее всего ошибка.
0
14
9 лет назад
0
Doc, дададада!
0
24
9 лет назад
0
DarkDes, собственно, я отталкиваюсь от двух источников - то, как это реализовано в Starcraft2 у Blizzard и то, как это было реализовано в свое время у меня в небольшом приватном проекте.
У Blizzard этот механизм вобще вынесен на уровень данных и как устроен сам движок остается только догадываться, а в моей версии похожий механизм был реализован довольно криво и с тех пор я уже успел серьезно поменять свою спецификацию такого механизма, но практической реализации новой спецификации еще не делал.
Суть в том, чтобы разобрать все сложные эффекты на простейшие составляющие и реализовать возможность собирать составные эффекты из кусочков.
Да, кстати, хочу уточнить кое что - слово "эффект" в данном контексте стоит воспринимать как "действие", может так будет понятнее о чем речь.
Бафы это отдельная тема, по которой мне не так много чего есть сказать. Разделить их можно на три основные категории - периодические, постоянные и маркеры. Маркеры - ничего не делающие бафы, позволяющие динамически сохранить какую-то дополнительную информацию об объекте, периодические - вызывают по таймеру привязанный к ним эффект. Сложнее всего с постоянными - они напрямую зависят от того, как реализованы характеристики объектов. Помимо этого еще стоит учитывать что у бафов может быть длительность действия, а может и не быть, что бафы могут иметь не только основной эффект, но и, например, эффект, который запускается только при снятии бафа с цели.
При этом наложение бафа на цель и снятие бафа в обход его жизненного цикла делаются, естественно, не напрямую, а через соответствующий тип эффектов.
Doc, но не стоит и уходить в противоположную сторону спектра, со скатыванием в макаронный код и месяцами дебага и рефакторинга после малейшего изменения. Неоднократно был свидетелем ситуации, когда небольшие и не особо критичные баги откладывали на "никогда" т.к. их фикс требовал изменения пары строк в тех частях кода, которые "трогать нельзя" т.к. это ведет к необходимости пересборки буквально всех модулей проекта и астрономических объемах работы для QA.
0
15
9 лет назад
0
prog:
Вот меня больше всего пугает конкретная реализация различных скиллов. Вернее сказать их разнообразие, например, геймдизайнер напридумывал кучу способностей герою, а как это реализовать ? (проект разрабатывается от желаемого результата, например ).
В варкрафте3 можно ведь свои бафы\скиллы\эффекты делать? как там это примерно реализовано? а то я в редакторе варика вообще дуб (
0
24
9 лет назад
0
DarkDes, в варкрафте3 можно было только менять характеристики уже существующих способностей или их копий. Кто хотел большего - использовал внутренний язык программирования jass.
В старкрафте 2 же это дело реализовано на порядок приятней, но и одновременно сложней. Не буду это расписывать - можно несколько дней убить и ничего понятней не станет.
Что касается придумок геймдизайнера - большая часть даже самых замысловатых способностей сводится к простейшим элементам - нанесение урона, наложение бафа, выбор объектов в области, запуск снаряда, взаимодействие с физическим движком, активация нескольких эффектов подряд, выбор одного из эффектов по условию, периодические действия и так далее. Причем этот список можно расширять по мере необходимости или дополнять уже существующие типы эффектов дополнительными параметрами.
Если хочешь, можешь сыграть роль геймдизайнера и придумать способность или даже набор способностей, а я распишу один из вариантов, как это можно было бы реализовать и какие бы базовые элементы для этого понадобились бы.
0
15
9 лет назад
0
Если хочешь, можешь сыграть роль геймдизайнера и придумать способность
На самом деле есть немного, все описывать смысла нет, но там действительно они конечны, но есть "одноразовые" :)
Вот например есть у меня в списке такие:
  1. "Превращает существо в золото. (если у существа меньше 100хп)"
  2. "Устремляется в указанном направлении, при этом нанося урон с силой 100 в своём радиусе." - т.е. на протяжении всего пути рывка и всем встретившимся противникам.
  3. "Замораживает выбранную цель" - это не стан, а именно заморозка.
  4. "Карающий взор. Наносит всем врагам в зоне видимости 300 урона."
  5. "Призывает ангела хранителя, характеристики которого ровно в 2 раза меньше, чем у Героя." - по сути создание юнита\Героя ?
Вот это некоторые примеры уникальных способностей, возможно, они разбиваются на более простые, что удаляет их уникальность.
А на jass можно закодить новые способности? Думал вот вместо игры сделать карту-прототип одной старой идеи, если конечно редактор карт такое позволит :)
0
24
9 лет назад
Отредактирован prog
0
"Превращает существо в золото. (если у существа меньше 100хп)"
проверка условий это отдельный вопрос, но суть примерно та-же - есть типы проверок и параметры
превращение в золото - зависит от того, какие требуются результирующие параметры, например можно фиксированное значение выдавать, можно по набору условий выдавать из списка возможных значений, а можно использовать какую-нибудь хитрую формулу
считаю что визуальная часть слушает события от логической и сама по себе знает что ей в том или ином случае делать, но по большому счету никто кроме здравого смысла не мешает и это запихнуть в эффекты
также для этого примера буду считать, что вознаграждение фиксированное, а проверка запаса здоровья делается на этапе каста способности - применить её на кого-то, у кого больше хп просто не даст и, соответственно, эффектам это проверять не нужно
получаем следующую цепочку эффектов:
1 несколько эффектов (список эффектов - эффекты 2 и 3)
2 получение ресурсов (тип ресурса - золото, цель - хозяин кастера, количество - xxx)
3 нанесение урона (кол-во урона - xxx, цель - цель заклинания, фатальный урон - да)
предположим, что нам надо варьировать кол-во золота в зависимости от ряда условий, проверка по прежнему на совести способности
  1. несколько эффектов (эффекты 2, 8 )
  2. проверка нескольких условий (список условий - 3, 5, список эффектов - 4, 6, прерывать проверку после первой удачной - да, действие по умолчанию - 7) работает путем последовательной проверки объектов-условий и активации соответствующих им объектов-эффектов, если условие вернет true, можно обрвать после первого нахождения true, можно не обрывать, можно назначить эффект, который будет вызван, если ни одно условие не вернет true
  3. условие типа "классификация цели" (цель - цель заклинания, ожидаемая классификация - механизм)
  4. получение ресурсов (тип ресурса - золото, цель - хозяин кастера, количество - 20)
  5. условие типа "классификация цели" (цель - цель заклинания, ожидаемая классификация - растение)
  6. получение ресурсов (тип ресурса - золото, цель - хозяин кастера, количество - 5)
  7. получение ресурсов (тип ресурса - золото, цель - хозяин кастера, количество - 10)
  8. нанесение урона (кол-во урона - xxx, цель - цель заклинания, фатальный урон - да)
предположим, что проверять подходит ли нам цель должны эффекты, а получаемое золото фиксировано
  1. проверка условия (условие - 2, эффект при true - 3, эффект при false - нет)
  2. условие типа "сравнение характеристик с константой" (цель - цель заклинания, характеристика - здоровье в %, операция - меньше или равно, значение - 10)
3 несколько эффектов (эффекты - 4, 5)
  1. получение ресурсов (тип ресурса - золото, цель - хозяин кастера, количество - xxx)
  2. нанесение урона (кол-во урона - xxx, цель - цель заклинания, фатальный урон - да)
по остальным распишу позже, если еще будет интересно
А на jass можно закодить новые способности? Думал вот вместо игры сделать карту-прототип одной старой идеи, если конечно редактор карт такое позволит :)
И да и нет. Данные способностей задаются только в редакторе объектов и код на них никак не повлияет, но игрока можно обмануть - создать способность, которая ничего не делает и отлавливать её применение, после чего делать то, что нужно. Есть один нюанс - применить способность триггерно из воздуха нельзя, потому если нужно использовать в качестве составляющих своей способности какие-то из родных для движка, приходится создавать невидимого юнита, который получает приказ применить способность, после чего удаляется. Короче куча мороки - быстрее игру написать имея некоторые навыки и план, чем с нуля во всех нюансах разобраться.
Для сравнения, во втором старкрафте все намного веселей - то что я привел в качестве примера там выглядело бы почти так-же - как набор данных в редакторе данных и ни строчки настоящего кода.
upd: не знаю зачем я написал условие <=10% если заказ был <100хп чистыми, но суть та-же
0
15
9 лет назад
0
prog:
Ухх сколько текста)) Честно говоря думал, что будет проще немного :)
Я бы наверно сделал какой-нибудь класс способности (интерфейс, абстрактный) и затем реализовал конкретную способность, где в каком-нибудь логическом методе делал бы проверку на xp и подачу золота. (кстати, золота получить можно не больше 100 т.к. эта способность по сути переводит хп врага в золото).
быстрее игру написать имея некоторые навыки и план, чем с нуля во всех нюансах разобраться.
Ясно .. а я то надеялся по быстрому зафигачить карту-прототип для проверки механики и правки баланса. Сейчас у меня есть немного информации о игре и таблицы всякие, но давненько не занимался этим проектом :(
Если кому интересно, то этот загадочный проект что-то вроде PvE вертикальной доты :D
0
24
9 лет назад
Отредактирован prog
0
DarkDes, даже не знаю что сказать - для меня идея разбиеня сложных способностей на элементарные эффекты выглядит настолько очевидной, что меня приводят в легкий ступор утверждения будто это очень сложно. И код намного чище получается - вместо бесконечного количества классов, каждый из которых описывает отдельную способность, получаем небольшой набор классов, каждый из которых выполняет свою сугубо специализарованную задачу. А если еще и довести дело до конца и вынести из кода все данные, получаем возможность править баланс не перекомпилируя ни строчки кода, да и создавать новых героев и способости тоже вполне реально, если они вписываются в уже существующие базовые механики.
0
29
9 лет назад
0
prog, вот сделали так в ск2 и что остановило большинство людей от его модмейкинга? Преслувотое разбиение и охрененно сложный редактор объектов
0
15
9 лет назад
0
prog, просто обычным текстом это выглядит сложно, а вот код был бы немного понятнее. Конечно я понимаю, что лучше все способности разбить так, чтобы потом где-нибудь в текстовом файле, где хранится реестр скилов, было проще вводить новые и править их, да ещё и убер-крутые штуки выделывать :)
Написать парсер\псевдо_язык_программирования - это конечно задачка, но вполне реализуемая.
Doc, не знаю как там в редакторе СК2, но эта сложность влияет на гибкость? Т.е. скажем Рейнору можно различные способности новые написать? Вроде Heroes of the Storm на движке СК2, а там много героев и способностей, наверно, там вообще что угодно можно сделать или нет ?
0
24
9 лет назад
Отредактирован prog
0
Doc, а еще лень, привычка к варкрафту и отсутствие информации. Ну и главная проблема у новичков это не столько само разбиение, сколько система агентов/актеров, которые отвечают за отображение всей визуальной части и по которым практически нет внятной документации даже на англоязычных ресурсах.
DarkDes, я в редакторе ск2 делал систему пошаговой игры на доске, в которой кодом было реализовано по большому счету только отображение таймера хода в интерфейсе и пара хаков, позволяющих обойти мелкие, но неприятные недостатки возможностей движка - все остальное было сделано в редакторе данных. Причем таймер тоже был на данные завязан, код отвечал только за синхронизацию интерфейса и таймера. Потом, конечно, кода поднакопилось, когда я захотел еще дальше за пределы базовых возможностей движка выйти.
Что касается интерпретируемого языка - во-первых в идеале он тоже нужен, просто для хранения данных это избыточное решение, а во-вторых намного проще интегрировать тот-же Lua или даже джаваскрипт, чем писать что-то свое.
0
29
9 лет назад
0
Написать парсер\псевдо_язык_программирования - это конечно задачка, но вполне реализуемая.
я так и сделал у себя в одной ммошке, свой скриптовый язык, который спелы реализовывал на стороне сервера
Чтобы оставить комментарий, пожалуйста, войдите на сайт.