This page has been translated automatically.
Видеоуроки
Интерфейс
Основы
Продвинутый уровень
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Профессиональный уровень (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Редактор UnigineEditor
Обзор интерфейса
Работа с ассетами
Контроль версий
Настройки и предпочтения
Работа с проектами
Настройка параметров ноды
Setting Up Materials
Настройка свойств
Освещение
Sandworm
Использование инструментов редактора для конкретных задач
Расширение функционала редактора
Встроенные объекты
Ноды (Nodes)
Объекты (Objects)
Эффекты
Декали
Источники света
Geodetics
World-ноды
Звуковые объекты
Объекты поиска пути
Player-ноды
Программирование
Основы
Настройка среды разработки
Примеры использования
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Плагины
Форматы файлов
Материалы и шейдеры
Rebuilding the Engine Tools
Интерфейс пользователя (GUI)
Двойная точность координат
API
Animations-Related Classes
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
VR-Related Classes
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Учебные материалы

Управление геймплеем

Давайте создадим компонент LevelManager для управления геймплеем, состояниями уровней и пользовательским интерфейсом. Менеджер создает графический пользовательский интерфейс и показывает время, оставшееся до окончания игры. Он также проверяет и показывает количество объектов, которые осталось убрать с игровой площадки.

Шаг 1. Настройка таймера и пользовательского интерфейса игры#

Нода с назначенным компонентом LevelManager должна присутствовать в мире, чтобы правила вступили в силу. Она будет управлять таймером и обновлять пользовательский интерфейс виджета для игры.

  1. Создайте новый компонент C++ и назовите его LevelManager.
  2. Напишите следующий код в среде IDE и сохраните решение. Создайте и запустите решение, чтобы сгенерировать файл свойств для компонента.

    Примечание
    Обратите внимание, что метод init() компонента LevelManager настроен на вызов во 2-м порядке, чтобы убедиться, что он инициализирован после компонента ObjectGenerator, который создает и привязывает к нему все физические объекты (порядок инициализации ObjectGenerator установлен на 1-й).
    LevelManager.h (С++)
    #pragma once
    #include <UnigineComponentSystem.h>
    
    #include <UnigineWidgets.h>
    #include <UnigineGame.h>
    #include <UnigineString.h>
    
    class LevelManager : public Unigine::ComponentBase
    {
    public:
    	// declare constructor and destructor for our class and define a property name. 
    	COMPONENT_DEFINE(LevelManager, ComponentBase)
    	// declare methods to be called at the corresponding stages of the execution sequence
    	COMPONENT_INIT(init, 2); // 2nd initialization order
    	COMPONENT_UPDATE(update);
    	COMPONENT_SHUTDOWN(shutdown);
    	// level timer
    	PROP_PARAM(Float, timer, 100.0f);
    	void decPhysicalObjectsNum();
    	void setEndGameCallback(Unigine::CallbackBase *callback) { endGameEvent = callback; }
    
    protected:
    	void init();
    	void update();
    	void shutdown();
    
    	void initGUI();
    
    private:
    	Unigine::CallbackBase *endGameEvent = nullptr;
    
    	bool isCounting = true;
    
    	int physicalObjectsNum;
    
    	Unigine::WidgetLabelPtr widget_timer, widget_goal;
    
    	Unigine::ObjectTextPtr endText;
    };
    LevelManager.cpp (С++)
    #include "LevelManager.h"
    
    using namespace Unigine;
    using namespace Math;
    
    REGISTER_COMPONENT(LevelManager);
    
    void LevelManager::init()
    {
    	LevelManager::initGUI();
    
    	// find the object text node of the widget
    	endText = checked_ptr_cast<ObjectText>(Game::getPlayer()->findNode("header_text", 1));
    
    	// count dynamic objects in the level
    	physicalObjectsNum = node->getNumChildren();
    }
    
    void LevelManager::initGUI()
    {
    	// get a GUI pointer
    	GuiPtr gui = Gui::getCurrent();
    
    	// create a label widget and set up its parameters
    	widget_timer = WidgetLabel::create(gui, "Time Left:");
    	widget_timer->setPosition(10, 10);
    	widget_timer->setFontColor(vec4_red);
    
    	widget_goal = WidgetLabel::create(gui, "Objects Left: ");
    	widget_goal->setPosition(10, 30);
    	widget_goal->setFontColor(vec4_blue);
    
    	// add widgets to the GUI
    	gui->addChild(widget_timer, Gui::ALIGN_OVERLAP);
    	gui->addChild(widget_goal, Gui::ALIGN_OVERLAP);
    }
    
    void LevelManager::update()
    {
    	// decrease the timer
    	if (isCounting)
    	{
    		timer = timer - Game::getIFps();
    		if (timer <= 0)
    		{
    			//set end game text
    			if (endText) endText->setText("Game Over");
    			if (endGameEvent) endGameEvent->run();
    			isCounting = false;
    		}
    	}
    
    	// show the current time and objects left to clear
    	if (isCounting)
    	{
    		widget_timer->setText(String::format("Time Left: %.2f s", timer.get()));
    		widget_goal->setText(String::format("Objects Left: %d", physicalObjectsNum));
    	}
    	//hide the widgets on endgame
    	else
    	{
    		widget_timer->setEnabled(false);
    		widget_goal->setEnabled(false);
    	}
    	
    	//win
    	if (physicalObjectsNum <= 0)
    	{
    		if (endText) endText->setText("Success!");
    		if (endGameEvent) endGameEvent->run();
    		isCounting = false;
    	}
    }
    
    void LevelManager::shutdown()
    {
    	widget_timer.deleteLater();
    	widget_goal.deleteLater();
    }
    
    void LevelManager::decPhysicalObjectsNum()
    {
    	physicalObjectsNum--;
    }
  3. Переключитесь обратно на UnigineEditor и создайте новую Dummy Node с именем level_manager и поместите ее где-нибудь в мире.
  4. Добавьте компонент LevelManager к ноде level_manager через окно Parameters в UnigineEditor.

Мы создали системный графический интерфейс с помощью API из кода. Альтернативный метод заключается в использовании файлов пользовательского интерфейса.

Шаг 2. Обнаружение физических объектов#

Только дочерние ноды level_manager должны быть удалены триггером Kill Zone. Давайте добавим каждый физический объект, который мы создали ранее, в качестве дочернего элемента к ноде level_manager. Чтобы правила функционировали должным образом, вам также необходимо добавить новое условие и вызов метода к компоненту KillZone, который проверяет, есть ли у входящей в зону ноды родительский элемент с подключенным компонентом LevelManager.

  1. Откройте компонент KillZone в вашей IDE, добавьте поле levelManager и замените содержимое функции обратного вызова Enter в соответствии со следующим кодом.

    KillZone.h (C++)
    #pragma once
    #include <UnigineComponentSystem.h>
    
    #include <UnigineWorlds.h>
    //========================== NEW - BEGIN ===============================
    #include "LevelManager.h"
    //=========================== NEW - END ================================
    
    class KillZone : public Unigine::ComponentBase
    {
    public:
    	// declare constructor and destructor for our class and define a property name. 
    	COMPONENT_DEFINE(KillZone, ComponentBase)
    	// declare methods to be called at the corresponding stages of the execution sequence
    	COMPONENT_INIT(init);
    
    protected:
    	void init();
    	
    	void enterEventHandler(const Unigine::NodePtr &target);
    
    private:
    	// the area into which an object should fall
    	Unigine::WorldTriggerPtr trigger;
    //========================== NEW - BEGIN ===============================
    	LevelManager *levelManager;
    //=========================== NEW - END ================================
    };
    KillZone.cpp (C++)
    #include "KillZone.h"
    
    using namespace Unigine;
    
    REGISTER_COMPONENT(KillZone);
    
    void KillZone::init()
    {
    	trigger = checked_ptr_cast<WorldTrigger>(node);
    
    	// subscribe for the Enter event (when an object enters the area)
    	if (trigger)
    		trigger->getEventEnter().connect(this, &KillZone::enterEventHandler);
    }
    
    void KillZone::enterEventHandler(const NodePtr &target)
    {
    //========================== NEW - BEGIN ===============================
    	levelManager = getComponentInParent<LevelManager>(target);
    	// check if the parent node has a LevelManager component attached
    	if (levelManager)
    	{
    		// delete the entered node and decrease the amount of physical objects
    		levelManager->decPhysicalObjectsNum();
    		target.deleteLater();
    	}
    //=========================== NEW - END ================================
    }
  2. Давайте установим ноду level_manager в качестве родительской ноды для физических объектов. Откройте компонент ObjectGenerator в вашей IDE и замените код в соответствующих файлах следующим. Сохраните код в своей среде разработки, создайте и запустите решение для восстановления файла свойств для компонента и вернитесь к UnigineEditor.

    ObjectGenerator.h (C++)
    #include <UnigineComponentSystem.h>
    #include <UnigineGame.h>
    
    #pragma once
    class ObjectGenerator : public Unigine::ComponentBase
    {
    
    public:
    	COMPONENT_DEFINE(ObjectGenerator, ComponentBase);
    	COMPONENT_INIT(init, 1); // 1st initialization order
    //========================== NEW - BEGIN ===============================
    	PROP_PARAM(Node, levelManager);
    //=========================== NEW - END ================================
    protected:
    	void init();
    };
    ObjectGenerator.cpp (C++)
    #include "ObjectGenerator.h"
    #include <UniginePrimitives.h>
    
    using namespace Unigine;
    
    REGISTER_COMPONENT(ObjectGenerator);
    
    void ObjectGenerator::init()
    {
    //========================== NEW - BEGIN ===============================
    	if (levelManager)
    	{
    //=========================== NEW - END ================================
    		// cube 
    		ObjectMeshDynamicPtr box = Primitives::createBox(Math::vec3(1.0f));
    		//========================== NEW - BEGIN ===============================
    		box->setParent(levelManager);
    		//========================== NEW - END ===============================
    		box->setTriggerInteractionEnabled(1);
    		box->setIntersection(1, 0);
    		box->setIntersectionMask(0x00000080, 0); // check the BulletIntersection bit
    		box->setWorldTransform(translate(Math::Vec3(0.5f, 7.5f, 1.0f)));
    		box->setMaterialPath("materials/mesh_base_0.mat", "*");
    		box->setName("box");
    		Unigine::BodyRigidPtr bodyBox = BodyRigid::create(box);
    		ShapeBoxPtr shapeBox = ShapeBox::create(bodyBox, Math::vec3(1.0f));
    		ShapeSphere::create(bodyBox, 0.5f);
    		bodyBox->setShapeBased(0);
    		bodyBox->setMass(2.0f);
    
    		// sphere
    		ObjectMeshDynamicPtr sphere = Primitives::createSphere(0.5f, 9, 32);
    		//========================== NEW - BEGIN ===============================
    		sphere->setParent(levelManager);
    		//========================== NEW - END ===============================
    		sphere->setTriggerInteractionEnabled(1);
    		sphere->setIntersection(1, 0);
    		sphere->setIntersectionMask(0x00000080, 0); // check the BulletIntersection bit
    		sphere->setWorldTransform(translate(Math::Vec3(4.5f, 5.5f, 1.0f)));
    		sphere->setMaterialPath("materials/mesh_base_1.mat", "*");
    		sphere->setName("sphere");
    		BodyRigidPtr bodySphere = BodyRigid::create(sphere);
    		ShapeSphere::create(bodySphere, 0.5f);
    		bodySphere->setShapeBased(0);
    		bodySphere->setMass(2.0f);
    
    		// capsule
    		ObjectMeshDynamicPtr capsule = Primitives::createCapsule(0.5f, 1.0f, 9, 32);
    		//========================== NEW - BEGIN ===============================
    		capsule->setParent(levelManager);
    		//========================== NEW - END ===============================
    		capsule->setTriggerInteractionEnabled(1);
    		capsule->setIntersection(1, 0);
    		capsule->setIntersectionMask(0x00000080, 0); // check the BulletIntersection bit
    		capsule->setWorldTransform(translate(Math::Vec3(4.5f, 0.5f, 3.0f)));
    		capsule->setMaterialPath("materials/mesh_base_2.mat", "*");
    		capsule->setName("capsule");
    		BodyRigidPtr bodyCapsule = BodyRigid::create(capsule);
    		ShapeCapsule::create(bodyCapsule, 0.5f, 1.0f);
    		bodyCapsule->setShapeBased(0);
    		bodyCapsule->setMass(2.0f);
    //========================== NEW - BEGIN ===============================
    	}
    //=========================== NEW - END ================================
    }
  3. Выберите ноду object_generator в окне World Nodes и перетащите ноду level_manager в соответствующее поле свойства ObjectGenerator.

  4. Сохраните изменения в мире. Перейдите к File->Save World или нажмите горячую клавишу Ctrl+S.
  5. Переключитесь на свой IDE. Сохраните изменения в решении, а затем создайте и запустите игру, чтобы увидеть геймплей в действии.
Последнее обновление: 29.11.2023
Build: ()