创建控制字符
步骤1。参与机器人模型的物理#
具有复杂的 3D模型的机器人将代表游戏中的可玩角色。我们已经导入了带有Skinned Mesh的Node Reference, 飞翔的动画,以及机器人的材质。
机器人必须能够在游戏区域内移动,并与静态物体和物理物体发生碰撞。为了做到这一点,它应该有一个物理的 body和一个碰撞形状来接近它的体积。
-
将导入的programming_quick_start\character\robot\robot.node从Asset Browser直接拖到Editor Viewport,放置在Play Area的地板上。
-
选中了robot节点后,单击Parameters窗口的Reference部分中的Edit。Node Reference将打开,当前的选择将聚焦于robot ObjectMeshSkinned节点。现在切换到Parameters窗口的Physics选项卡,并为选定的ObjectMeshSkinned分配Rigid主体。
- 将LDamping参数设置为 5.0,确保机器人会随着时间的推移而失去速度。
-
向下滚动到Shapes部分,并向body添加一个Capsule shape。
胶囊形状将被用作与世界上其他物体碰撞的近似体积。
步骤2。设置控制#
我们将一个线性脉冲对身体移动机器人键盘 WASD 键。机器人的运动将决定根据相机的方向。同时,我们限制了基于物理旋转和垂直运动,以避免不必要的控制行为。
为了使机器人朝向光标,我们将使用交集类型中的一个,称为World Intersection。它将从光标位置到地板的一条线,以得到一个交点,作为机器人旋转的参考点。您可以阅读更多关于管理各种交叉口在这里。
管理键盘和鼠标输入的最佳方法是使用Input类。它使您能够检查按钮的状态并获得当前鼠标坐标。输入处理的替代方法在这里描述 。
让我们使用 C++ 组件系统 来实现这个逻辑。 我们将 创建一个 C++ 组件 并将其分配给机器人在世界中的节点。
- 要开始编写代码,我们应该在IDE中打开我们的项目。转到SDK Browser并选择Open Code IDE。
-
在IDE创建一个新的c++类(*.h和*.cpp文件)和PlayerController调用它。确保它从Unigine::ComponentBase类继承。
复制下面的代码并将其粘贴到项目中的相应文件中,并将其保存到IDE中。
推荐使用Visual Studio作为默认的IDE。#pragma once #include <UnigineComponentSystem.h> #include <UnigineGame.h> #include <UnigineControls.h> #include <UnigineStreams.h> #include <UniginePlayers.h> #include <UnigineWorld.h> #include <UnigineConsole.h> #include <UnigineMathLib.h> #include <UnigineRender.h> class PlayerController : public Unigine::ComponentBase { public: // declare constructor and destructor for our class and define a property name. COMPONENT_DEFINE(PlayerController, ComponentBase) // declare methods to be called at the corresponding stages of the execution sequence COMPONENT_INIT(init); COMPONENT_UPDATE_PHYSICS(updatePhysics); protected: void init(); void updatePhysics(); private: void move(const Unigine::Math::vec3& direction); Unigine::BodyRigidPtr rigid; Unigine::PlayerPtr player; // a WorldIntersection object to store the information about the intersection Unigine::WorldIntersectionPtr intersection = Unigine::WorldIntersection::create(); Unigine::Math::Vec3 pos; };
#include "PlayerController.h" using namespace Unigine; using namespace Math; REGISTER_COMPONENT(PlayerController); void PlayerController::init() { player = Game::getPlayer(); if (node) { rigid = node->getObjectBodyRigid(); if (rigid) { rigid->setAngularScale(vec3(0.0f, 0.0f, 0.0f)); // restricting the rotation rigid->setLinearScale(vec3(1.0f, 1.0f, 0.0f)); // restricting Z movement rigid->setMaxLinearVelocity(8.0f); // clamping the max linear velocity } } } void PlayerController::updatePhysics() { if (!Console::isActive()) // do not process input if the console is shown { // check if W key is pressed if (Input::isKeyPressed(Input::KEY::KEY_W)) move(player->getWorldDirection(MathLib::AXIS::AXIS_Y)); // move forward // check if S key is pressed if (Input::isKeyPressed(Input::KEY::KEY_S)) move(player->getWorldDirection(MathLib::AXIS::AXIS_NY)); // move backward // check if A key is pressed if (Input::isKeyPressed(Input::KEY::KEY_A)) move(player->getWorldDirection(MathLib::AXIS::AXIS_NX)); // move left // check if D key is pressed if (Input::isKeyPressed(Input::KEY::KEY_D)) move(player->getWorldDirection(MathLib::AXIS::AXIS_X)); // move right // finding the positions of the cursor and the point moved 100 units away in the camera forward direction ivec2 mouse = Input::getMousePosition(); Vec3 p0 = player->getWorldPosition(); Vec3 p1 = p0 + Vec3(player->getDirectionFromMainWindow(mouse.x, mouse.y)) * 100; // casting a ray from p0 to p1 to find the first intersected object ObjectPtr obj = World::getIntersection(p0, p1, 1, intersection); // the first bit of the intersection mask is set to 1, the rest are 0s // finding the intersection position, creating a transformation matrix to face this position and setting the transform matrix for the body preserving current angular and linear velocities if (obj && rigid) { pos = intersection->getPoint(); pos.z = rigid->getTransform().getTranslate().z; // project the position vector to the Body Rigid pivot plane Mat4 transform = Math::setTo(rigid->getTransform().getTranslate(), pos, vec3_up, AXIS::AXIS_Y); rigid->setPreserveTransform(transform); // turn the character's body } } } // moving the rigid body with linear impulse in the specified direction void PlayerController::move(const Unigine::Math::vec3& direction) { // direction is a normalized camera axis vector if (rigid) // direction is a normalized camera axis vector rigid->addLinearImpulse(direction); }
-
在你使用c++组件之前,你应该初始化组件系统。修改AppSystemLogic类的init()方法,如下所示(AppSystemLogic.cpp文件)。为了方便,也可以启用自动NodeReference unpacking。
#include <UnigineComponentSystem.h> using namespace Unigine; int AppSystemLogic::init() { // initialize ComponentSystem and register all components ComponentSystem::get()->initialize(); // unpack node references to find child nodes by name World::setUnpackNodeReferences(true); return 1; }
- 通过您的IDE(在Visual Studio中按Ctrl + F5)构建和运行项目,组件系统将为组件生成一个属性文件。
-
切换回UnigineEditor并选择robot节点(ObjectMeshSkinned) World Nodes窗口。然后单击Add New Property Parameters窗口和分配新生成PlayerController财产。
确保将属性分配给 NodeReference 内的 ObjectMeshSkinned 节点!
步骤3。完成并运行#
- 关闭机器人节点每个表面的 Intersection 检测以忽略与机器人自身表面的交叉点,因为我们不希望它在机器人的旋转实现中出现。
-
对于,世界中每个 wall对象,转到Parameters窗口,并在Node选项卡中找到Surfaces部分。选择网格的box表面并打开一个Intersection Mask窗口。取消第一个Intersection Mask 0位,以确保墙壁不会影响玩家的角色转弯。
- 要使光标始终可见,请转到 Windows->Settings->Controls 部分,将 Mouse Handle 模式更改为 User。 您还可以通过 API 控制光标。
- 通过File->Save World(或按Ctrl+S热键)保存世界和机器人的更改。
在你的IDE中构建并运行游戏,以尝试机器人的控制。