This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
基础
专业(SIM)
UnigineEditor
界面概述
资源工作流程
Version Control
设置和首选项
项目开发
调整节点参数
Setting Up Materials
设置属性
照明
Sandworm
使用编辑器工具执行特定任务
如何擴展編輯器功能
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
C++
C#
UnigineScript
统一的Unigine着色器语言 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

碰撞接触处理

Sometimes you may need to implement your own contact handler for colliding physical bodies. In this example, you will learn how to visualize contact points, display debug information, and add a hit effect (sparks) at the point of impact. 有时候您可能需要实现您自己的接触处理程序来碰撞物理物体。在本例中,您将学习如何可视化接触点、显示调试信息以及在撞击点添加撞击效果(火花)。

Preparing the content
准备内容#

To create sparks, we are going to use the VFX add-on (you can download it on https://store.unigine.com).要创建火花,我们将使用 VFX附加组件(您可以在 https://store.unigine.com 网站上下载)。

Create a new project in UNIGINE SDK Browser and import the VFX add-on to it.在UNIGINE SDK浏览器中创建一个新项目导入VFX插件

Open the project in the UnigineEditor and add some primitive objects to the default scene.在UnigineEditor中打开项目,并在默认场景中添加一些基本对象。

Let's get some information about the contacts the blue box will have with other objects.让我们获取一些关于蓝框与其他物体的接触的信息。

Making the Objects Collidable
使物体可碰撞#

The objects in the scene are dynamic ones, so to be able to collide they need a body and a collision shape. Add a Rigid body and a shape to each object via the Physics tab of the Parameters window.场景中的对象是动态的,所以为了能够碰撞,它们需要一个和一个碰撞形状。通过Parameters窗口的Physics选项卡为每个对象添加一个Rigid body和一个形状。

Collisions are available for static objects as well (like buildings or ground) — simply enable the Collision option for the corresponding surface.碰撞也可用于静态对象(如建筑物或地面)-只需为相应的表面启用Collision选项。

Enabling High Priority Contacts
启用高优先级接触#

On collision, contacts are distributed randomly between the interacting bodies to optimize performance: some are handled by the first body, others by the second. For a body, contacts that it handles itself are internal (access to them is fast), and contacts handled by other bodies are external.在碰撞时,接触在相互作用的物体之间随机分布以优化性能:一些物体由第一个物体处理,另一些由第二个物体处理。对于一个体来说,它自己处理的接触是 internal(访问它们是快速的),其他体处理的接触是external

注意
You can iterate through all contacts for a body, but for better performance it is recommended to process only internal ones.您可以遍历物体的所有接触,但为了获得更好的性能,建议只处理内部接触。

The box is a high-priority body for us and we want to track its collisions with maximum efficiency. We can make the box handle most of its contacts itself (so that most of them are internal). To do so, select the box and check High Priority Contacts in the Physics tab (or do it via code).该盒子对我们来说是一个高优先级的物体,我们希望以最大的效率跟踪它的碰撞。 我们可以让盒子自己处理大部分触点(这样大多数触点都是内部的)。 为此,请选中该框并选中Physics选项卡中的High Priority Contacts(或通过代码执行此操作)。

Creating a Hit Effect Node
创建命中效果节点#

We will use a Particle System to create a node that simulates sparks.我们将使用粒子系统来创建一个模拟火花的节点。

  1. In the UnigineEditor, click Create -> Particle System -> Particles. Place the object somewhere in the world, rename it to sparks, and adjust its parameters:在UnigineEditor中,单击Create -> Particle System -> Particles。将对象放置在世界的某个地方,将其重命名为sparks,并调整其参数:

    • Number Per Spawn = 10
    • Radius = 0.02
    • Life Time = 0.2
    • Period = inf
    • Duration = 0
  2. In the Surface Material section, assign library_spark1.mat material (it is located in the data -> vfx -> materials -> library_vfx folder of your project).Surface Material部分中,分配library_spark1.mat材料(它位于项目的data -> vfx -> materials -> library_vfx文件夹中)。
  3. Switch to the data folder in the Asset Browser. Right-click the sparks node and select Create a Node Reference. A sparks.node file will be generated in the data folder.在资源浏览器中切换到data文件夹。右键单击sparks节点并选择Create a Node Reference。将在data文件夹中生成一个sparks.node文件。
  4. Delete the sparks object from the scene, we no longer need it since it will be loaded via code.从场景中删除sparks对象,我们不再需要它,因为它将通过代码加载。

Algorithm Description
算法描述#

The algorithm we are going to use can be described as follows:我们将要使用的算法可以描述如下:

  1. We create three variables (lastContactTime, lastContactPoint, lastContactInfo) to keep the time and position of the last occurred contact and some info about it.我们创建了三个变量(lastContactTime, lastContactPoint, lastContactInfo)来保存最后一次发生接触的时间和位置以及有关它的一些信息。
  2. We subscribe for an event to perform some actions when each contact emerges. The handler function (OnContactEnter()) takes the body and the index of the contact.我们订阅一个事件,以便在每个接触出现时执行一些操作。处理程序函数(OnContactEnter())接受接触的主体和索引。
  3. If the contact is internal, we save its time and position.如果接触是内部的,我们保存了它的时间和位置。
  4. We get both physical bodies participating in this contact (body0, body1). We check if our box has hit another physical object.我们让两个实体都参与到这个接触中(body0, body1)。我们检查盒子是否碰到了另一个物体。

    • If any of body0 and body1 exists (and it is not the body of our box), then we have found this object. We get details about this body and render it in the viewport using Visualizer.如果body0body1中的任何一个存在(并且它不是盒子的主体),那么我们就找到了这个对象。我们获取这个主体的详细信息,并使用 Visualizer在视口中呈现它。
    • Otherwise, the box has hit some static object (a surface with the Collision option enabled). We make this surface highlighted in the viewport as well.否则,盒子会撞上某个静态对象(启用了Collision选项的表面)。我们也在视口中突出显示这个表面。
  5. We add details we are interested in (e.g., the contact impulse).我们添加我们感兴趣的细节(例如,接触脉冲)。
  6. We spawn a hit effect if the impulse is strong enough. Physics event handlers are called in the main thread, so it is safe to create nodes inside the OnContactEnter() function.如果冲动足够强烈,我们就会产生命中效果。物理事件处理程序在主线程中调用,因此在OnContactEnter()函数中创建节点是安全的。
  7. Finally, in the Update() method we display the info and create a slow motion effect for one second.最后,在Update()方法中,我们显示信息并创建一秒钟的慢动作效果。

Component Code
组件代码#

Let's use the C++ Component System to implement this logic. We will create a C++ component and assign it to the box node.让我们使用 c++组件系统来实现这个逻辑。我们将创建一个c++组件并将其分配给box节点。

  1. Open your project in an IDE.在IDE中打开项目。

    注意
    It is recommended to use Visual Studio as a default IDE.建议使用 Visual Studio作为默认IDE。
  2. In the IDE create a new C++ class (*.h and *.cpp files) and call it ContactsHandler. Make sure it inherits from the Unigine::ComponentBase class.在IDE中创建一个新的C++类(*.h*.cpp文件)并将其命名为ContactsHandler。确保它继承自Unigine::ComponentBase类。
  3. Copy the code below, paste it to the corresponding files in your project and save them in your IDE.复制下面的代码,将其粘贴到项目中的相应文件中,并将它们保存在IDE中。

    ContactsHandler.h (C++)
    #pragma once
    #include <UnigineComponentSystem.h>
    #include <UnigineGame.h>
    #include <time.h>
    
    class ContactsHandler :
    	public  Unigine::ComponentBase
    {
    public:
    	// declare constructor and destructor for our class and define a property name. 
    	COMPONENT_DEFINE(ContactsHandler, ComponentBase)
    		// declare methods to be called at the corresponding stages of the execution sequence
    		COMPONENT_INIT(init);
    	COMPONENT_UPDATE(update);
    
    	// A node that will be loaded on contact (a hit effect)
    	PROP_PARAM(File, hitEffect);
    
    	bool debug = true;
    
    protected:
    	void init();
    	void update();
    	void OnContactEnter(const Unigine::BodyPtr &, int);
    
    private:
    	// Time, position and some info of the last occurred contact
    	time_t lastContactTime;
    	Unigine::Math::vec3 lastContactPoint;
    	Unigine::String lastContactInfo;
    };
    ContactsHandler.cpp (C++)
    #include "ContactsHandler.h"
    #include <UnigineNode.h>
    #include <UnigineVisualizer.h>
    
    using namespace Unigine;
    using namespace Math;
    
    REGISTER_COMPONENT(ContactsHandler);
    
    void ContactsHandler::init()
    {
    	BodyPtr body = node->getObjectBody();
    	if (body)
    	{
    		// For debug purposes, we can render certain contacts depending on their type    
    		body->getEventContacts().connect(*this, [](const BodyPtr &b) {b->renderInternalContacts(); });
    		// subscription for contact event (when each contact emerges)
    		body->getEventContactEnter().connect(this, &ContactsHandler::OnContactEnter);
    	}
    }
    
    // This function takes the body and the index of the contact
    void ContactsHandler::OnContactEnter(const Unigine::BodyPtr &body, int num)
    {
    	// Enable Visualizer to see the rendered contact points
    	Visualizer::setEnabled(true);
    
    	if (body->isContactInternal(num))
    	{
    		if (debug)
    		{
    			// The time of the contact
    			lastContactTime = time(NULL);
    			// The position of the contact
    			lastContactPoint = body->getContactPoint(num);
    
    			// We get both physical bodies participating in this contact
    			BodyPtr body0 = body->getContactBody0(num);
    			BodyPtr body1 = body->getContactBody1(num);
    
    			BodyPtr touchedBody = NULL;
    
    			// We check if our object has hit another physical object.
    			// If any of the bodies exists and it's not the body of our object
    			// then we have found another physics-driven object that hit it
    			if (body0 && body0 != body) touchedBody = body0;
    			if (body1 && body1 != body) touchedBody = body1;
    
    			if (touchedBody)
    			{
    				// Our object has touched a physics-driven object.
    				// We save the info about the body
    				lastContactInfo = String::format("body %s of object %s ", touchedBody->getName(), touchedBody->getObject()->getName());
    				// Render it in the viewport
    				Visualizer::renderObject(touchedBody->getObject(), Math::vec4_blue, 0.5f);
    			}
    			else
    			{
    				// It has touched a surface with Collision enabled
    				lastContactInfo = String::format("surface #%d of object %s", body->getContactSurface(num), body->getContactObject(num)->getName());
    				// Highlighting the surface
    				Visualizer::renderObjectSurface(body->getContactObject(num), body->getContactSurface(num), Math::vec4_blue, 0.5f);
    			}
    
    			// You can add details you are interested in (e.g., the contact impulse)
    			lastContactInfo += String::format("\nimpulse: %f", body->getContactImpulse(num));
    		}
    
    		// We spawn a hit effect if the impulse is strong enough
    		if (body->getContactImpulse(num) > 0.3f && hitEffect)
    		{
    			NodePtr Effect = World::loadNode(hitEffect);
    			Effect->setPosition(Vec3(body->getContactPoint(num)));
    		}
    	}
    
    }
    
    void ContactsHandler::update()
    {
    	// Here we can display the info and create a slow motion effect for one second
    	if (debug)
    	{
    		if (int(time(NULL) - lastContactTime) < 1.0f)
    		{
    			// Slow motion effect
    			Game::setScale(0.3f);
    
    			Visualizer::renderMessage3D(lastContactPoint, Math::vec3_one, ("last contact: \n" + lastContactInfo).get(), Math::vec4_green, 2, 24);
    		}
    		else  // All the other time the speed will be normal
    		{
    			Game::setScale(1.0f);
    		}
    	}
    }
  4. Before you can use C++ components, you should initialize the Component System. Modify the init() method of the AppSystemLogic class as shown below (AppSystemLogic.cpp file).在您可以使用c++组件之前,您应该初始化组件系统。修改AppSystemLogic类的init()方法,如下所示(AppSystemLogic.cpp文件)。

    AppSystemLogic.cpp (C++)
    #include <UnigineComponentSystem.h>
    
    int AppSystemLogic::init()
    {
    	// initialize ComponentSystem and register all components
    	ComponentSystem::get()->initialize();
    
    	return 1;
    }
  5. Build and run the project via your IDE (press Ctrl + F5 in Visual Studio). The Component System will generate a property file (ContactsHandler.prop) in the data -> ComponentSystem folder of your project.通过IDE构建并运行项目(在Visual Studio中按Ctrl + F5)。组件系统将在项目的data -> ComponentSystem文件夹中生成一个属性文件(ContactsHandler.prop)。

Applying Logic to the Object
将逻辑应用于对象#

Switch back to the UnigineEditor and select the box node. Click Add New Property in the Parameters window and assign the newly generated ContactsHandler property. Specify the sparks.node asset to be loaded and spawned on collision.切换回UnigineEditor并选择box节点。在Parameters窗口中单击Add New Property,并分配新生成的1_ight属性。指定要在碰撞时加载和生成的sparks.node资源。

注意
You might need to increase the physics FPS and the number of iterations for better quality during slowdown. You can do this in the Settings tab of the UnigineEditor.您可能需要提高物理FPS和迭代次数,以便在减速时获得更好的质量。您可以在UnigineEditor的Settings选项卡中执行此操作。

Click Run to see the result. As the box is rolling around, the physics-driven objects and collidable surfaces it hits along the way are highlighted, and contact details are displayed near the contact point. Whenever a collision occurs, it triggers an asset spawning and a slow motion effect.单击Run查看结果。当盒子滚动时,物理驱动的物体和它一路上碰到的可碰撞表面会高亮显示,接触点附近会显示接触细节。每当发生碰撞时,它就会触发资源刷出和慢动作效果。

注意
All manipulations with physical bodies should belong to the UpdatePhysics() method.所有对实体的操作都应该属于UpdatePhysics()方法。

This is how you can easily track collisions and run necessary logic on time: spawn particles, play sounds, and destroy fracture objects.这就是您如何轻松跟踪碰撞并及时运行必要的逻辑:生成粒子,播放声音,并破坏断裂对象。

最新更新: 2024-12-13
Build: ()