This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
专业(SIM)
UnigineEditor
界面概述
资源工作流程
版本控制
设置和首选项
项目开发
调整节点参数
Setting Up Materials
设置属性
照明
Sandworm
使用编辑器工具执行特定任务
如何擴展編輯器功能
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
使用范例
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
材质和着色器
Rebuilding the Engine Tools
GUI
双精度坐标
应用程序接口
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
Tutorials

创建控制字符

让我们创建一个可玩的机器人,它可以在游戏区域内移动,并且 与物体或墙壁障碍物发生碰撞。我们的角色将飞到地板上方,并旋转到面向光标的位置。

步骤1。参与机器人模型的物理#

具有复杂的 3D模型的机器人将代表游戏中的可玩角色。我们已经导入了带有Skinned MeshNode Reference, 飞翔的动画,以及机器人的材质

机器人必须能够在游戏区域内移动,并与静态物体和物理物体发生碰撞。为了做到这一点,它应该有一个物理的 body和一个碰撞形状来接近它的体积。

  1. 将导入的programming_quick_start\character\robot\robot.nodeAsset Browser直接拖到Editor Viewport,放置在Play Area的地板上。

  2. 选中了robot节点后,单击Parameters窗口的Reference部分中的EditNode Reference将打开,当前的选择将聚焦于robot ObjectMeshSkinned节点。现在切换到Parameters窗口的Physics选项卡,并为选定的ObjectMeshSkinned分配Rigid主体。

  3. LDamping参数设置为 5.0,确保机器人会随着时间的推移而失去速度。
  4. 向下滚动到Shapes部分,并向body添加一个Capsule shape

胶囊形状将被用作与世界上其他物体碰撞的近似体积。

步骤2。设置控制#

我们将一个线性脉冲对身体移动机器人键盘 WASD 键。机器人的运动将决定根据相机的方向。同时,我们限制了基于物理旋转和垂直运动,以避免不必要的控制行为。

为了使机器人朝向光标,我们将使用交集类型中的一个,称为World Intersection。它将从光标位置到地板的一条线,以得到一个交点,作为机器人旋转的参考点。您可以阅读更多关于管理各种交叉口在这里

管理键盘和鼠标输入的最佳方法是使用Input类。它使您能够检查按钮的状态并获得当前鼠标坐标。输入处理的替代方法在这里描述

让我们使用 C++ 组件系统 来实现这个逻辑。 我们将 创建一个 C++ 组件 并将其分配给机器人在世界中的节点。

  1. 要开始编写代码,我们应该在IDE中打开我们的项目。转到SDK Browser并选择Open Code IDE
  2. 在IDE创建一个新的c++类(*.h*.cpp文件)和PlayerController调用它。确保它从Unigine::ComponentBase类继承。

  3. 复制下面的代码并将其粘贴到项目中的相应文件中,并将其保存到IDE中。

    注意
    推荐使用Visual Studio作为默认的IDE。
    PlayerController.h (C++)
    #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;
    
    };
    PlayerController.cpp (C++)
    #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);
    }
  4. 在你使用c++组件之前,你应该初始化组件系统。修改AppSystemLogic类的init()方法,如下所示(AppSystemLogic.cpp文件)。为了方便,也可以启用自动NodeReference unpacking

    AppSystemLogic.cpp (C++)
    #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;
    }
  5. 通过您的IDE(在Visual Studio中按Ctrl + F5)构建和运行项目,组件系统将为组件生成一个属性文件。
  6. 切换回UnigineEditor并选择robot节点(ObjectMeshSkinned) World Nodes窗口。然后单击Add New Property Parameters窗口和分配新生成PlayerController财产。

    注意
    确保将属性分配给 NodeReference 内的 ObjectMeshSkinned 节点!

步骤3。完成并运行#

  1. 关闭机器人节点每个表面的 Intersection 检测以忽略与机器人自身表面的交叉点,因为我们不希望它在机器人的旋转实现中出现。
  2. 对于,世界中每个 wall对象,转到Parameters窗口,并在Node选项卡中找到Surfaces部分。选择网格的box表面并打开一个Intersection Mask窗口。取消第一个Intersection Mask 0位,以确保墙壁不会影响玩家的角色转弯。

  3. 要使光标始终可见,请转到 Windows->Settings->Controls 部分,将 Mouse Handle 模式更改为 User。 您还可以通过 API 控制光标。
  4. 通过File->Save World(或按Ctrl+S热键)保存世界和机器人的更改。

在你的IDE中构建并运行游戏,以尝试机器人的控制。

注意
要从SDK浏览器运行项目的调试版本,请在Customize Run Options中启用Debug模式。
最新更新: 2024-02-27
Build: ()