UE4 只与指定的Actor发生碰撞

本文介绍如何在Unity使用Unreal Engine的原理,通过自定义碰撞过滤,实现角色与其分身之间的碰撞忽略,同时保持与其他角色的正常碰撞交互。通过修改世界设置和利用场景组件的唯一ID,确保特定角色的分身不会与自身发生碰撞。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个需求 分身功能 分身与角色自己不发生碰撞 但是与其他角色发生碰撞

因为不同的角色都是使用相同的碰撞通道 这就需要实现指定分身与自己不发生碰撞

参考 https://answers.unrealengine.com/questions/543433/view.html

直接上主要代码

WorldSetting.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/WorldSettings.h"
#include "MyWorldSettings.generated.h"

USTRUCT()
struct FIgnorePair
{
	GENERATED_USTRUCT_BODY()
public:
	TWeakObjectPtr<USceneComponent> obj1;
	TWeakObjectPtr<USceneComponent> obj2;

	bool operator ==(const FIgnorePair& V)
	{
		return obj1->GetUniqueID() == V.obj1->GetUniqueID() || obj2->GetUniqueID() == V.obj2->GetUniqueID();
	}
};
/**
 * 
 */
UCLASS()
class TESTCAR_API AMyWorldSettings : public AWorldSettings
{
	GENERATED_BODY()
public:
	AMyWorldSettings(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
public:

private:

	TArray<FIgnorePair> ignoreComponents;
public:
	const TArray<FIgnorePair>& GetIgnoreComponents();

	void IgnoreBetween(USceneComponent* a, USceneComponent* b);

	bool RemoveIgnoreBetween(USceneComponent* a, USceneComponent* b);
};

WorldSetting.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyWorldSettings.h"
#include "Engine/World.h"
#include "Physics/PhysicsFiltering.h"
#include "MyPlayerController.h"


//定义物理过滤函数
static PxFilterFlags PhysXSimFilterShaderPortal(PxFilterObjectAttributes attributes0, PxFilterData filterData0,
	PxFilterObjectAttributes attributes1, PxFilterData filterData1,
	PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
	AMyWorldSettings* settings = nullptr;
	
	if (AMyPlayerController::currentWorld && AMyPlayerController::currentWorld->GetWorldSettings())
	{
		settings = Cast<AMyWorldSettings>(AMyPlayerController::currentWorld->GetWorldSettings());
	}
	if (settings != nullptr)
	{
		const auto& ignoreComponents = settings->GetIgnoreComponents();

		for (int i = 0; i < ignoreComponents.Num(); ++i)
		{
			const auto& pair = ignoreComponents[i];
			if (pair.obj1.IsValid() && pair.obj2.IsValid())
			{
				if ((pair.obj1->GetUniqueID() == filterData0.word2 && pair.obj2->GetUniqueID() == filterData1.word2) ||
					(pair.obj2->GetUniqueID() == filterData0.word2 && pair.obj1->GetUniqueID() == filterData1.word2))
				{
					return PxFilterFlag::eKILL;
				}
			}
		}
	}
	//原有的方法
	bool k0 = PxFilterObjectIsKinematic(attributes0);
	bool k1 = PxFilterObjectIsKinematic(attributes1);

	PxU32 FilterFlags0 = (filterData0.word3 & 0xFFFFFF);
	PxU32 FilterFlags1 = (filterData1.word3 & 0xFFFFFF);

	if (k0 && k1)
	{
		//Ignore kinematic kinematic pairs unless they are explicitly requested
		if (!(FilterFlags0&EPDF_KinematicKinematicPairs) && !(FilterFlags1&EPDF_KinematicKinematicPairs))
		{
			return PxFilterFlag::eSUPPRESS;	//NOTE: Waiting on physx fix for refiltering on aggregates. For now use supress which automatically tests when changes to simulation happen
		}
	}

	bool s0 = PxGetFilterObjectType(attributes0) == PxFilterObjectType::eRIGID_STATIC;
	bool s1 = PxGetFilterObjectType(attributes1) == PxFilterObjectType::eRIGID_STATIC;

	//ignore static-kinematic (this assumes that statics can't be flagged as kinematics)
	// should return eSUPPRESS here instead eKILL so that kinematics vs statics will still be considered once kinematics become dynamic (dying ragdoll case)
	if ((k0 || k1) && (s0 || s1))
	{
		return PxFilterFlag::eSUPPRESS;
	}

	// if these bodies are from the same component, use the disable table to see if we should disable collision. This case should only happen for things like skeletalmesh and destruction. The table is only created for skeletal mesh components at the moment
#if !WITH_APEIRON && !PHYSICS_INTERFACE_LLIMMEDIATE
	if (filterData0.word2 == filterData1.word2)
	{
		check(constantBlockSize == sizeof(FPhysSceneShaderInfo));
		const FPhysSceneShaderInfo* PhysSceneShaderInfo = (const FPhysSceneShaderInfo*)constantBlock;
		check(PhysSceneShaderInfo);
		FPhysScene * PhysScene = PhysSceneShaderInfo->PhysScene;
		check(PhysScene);

		const TMap<uint32, TMap<FRigidBodyIndexPair, bool> *> & CollisionDisableTableLookup = PhysScene->GetCollisionDisableTableLookup();
		TMap<FRigidBodyIndexPair, bool>* const * DisableTablePtrPtr = CollisionDisableTableLookup.Find(filterData1.word2);
		if (DisableTablePtrPtr)		//Since collision table is deferred during sub-stepping it's possible that we won't get the collision disable table until the next frame
		{
			TMap<FRigidBodyIndexPair, bool>* DisableTablePtr = *DisableTablePtrPtr;
			FRigidBodyIndexPair BodyPair(filterData0.word0, filterData1.word0); // body indexes are stored in word 0
			if (DisableTablePtr->Find(BodyPair))
			{
				return PxFilterFlag::eKILL;
			}

		}
	}
#endif

	// Find out which channels the objects are in
	ECollisionChannel Channel0 = GetCollisionChannel(filterData0.word3);
	ECollisionChannel Channel1 = GetCollisionChannel(filterData1.word3);

	// see if 0/1 would like to block the other 
	PxU32 BlockFlagTo1 = (ECC_TO_BITFIELD(Channel1) & filterData0.word1);
	PxU32 BlockFlagTo0 = (ECC_TO_BITFIELD(Channel0) & filterData1.word1);

	bool bDoesWantToBlock = (BlockFlagTo1 && BlockFlagTo0);

	// if don't want to block, suppress
	if (!bDoesWantToBlock)
	{
		return PxFilterFlag::eSUPPRESS;
	}



	pairFlags = PxPairFlag::eCONTACT_DEFAULT;

	//todo enabling CCD objects against everything else for now
	if (!(k0 && k1) && ((FilterFlags0&EPDF_CCD) || (FilterFlags1&EPDF_CCD)))
	{
		pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT | PxPairFlag::eSOLVE_CONTACT;
	}


	if ((FilterFlags0&EPDF_ContactNotify) || (FilterFlags1&EPDF_ContactNotify))
	{
		pairFlags |= (PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eNOTIFY_CONTACT_POINTS);
	}


	if ((FilterFlags0&EPDF_ModifyContacts) || (FilterFlags1&EPDF_ModifyContacts))
	{
		pairFlags |= (PxPairFlag::eMODIFY_CONTACTS);
	}

	return PxFilterFlags();
}

AMyWorldSettings::AMyWorldSettings(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	GSimulationFilterShader = PhysXSimFilterShaderPortal;
}


const TArray<FIgnorePair>& AMyWorldSettings::GetIgnoreComponents()
{
	// TODO: insert return statement here
	return ignoreComponents;
}

void AMyWorldSettings::IgnoreBetween(USceneComponent* a, USceneComponent* b)
{
	FIgnorePair ComponentPair;
	ComponentPair.obj1 = a;
	ComponentPair.obj2 = b;
	

	ignoreComponents.Add(ComponentPair);
}
bool AMyWorldSettings::RemoveIgnoreBetween(USceneComponent* a, USceneComponent* b)
{
	for (auto ComponentPair : ignoreComponents)
	{
		if (ComponentPair.obj1->GetUniqueID() == a->GetUniqueID() ||
			ComponentPair.obj2->GetUniqueID() == b->GetUniqueID())
		{
			ignoreComponents.Remove(ComponentPair);
			return true;
		}
	}
	return false;
}

 

最后 测试工程链接

https://download.youkuaiyun.com/download/maxiaosheng521/11177614

### UE4 碰撞检测勾选项设置教程 在 Unreal Engine 4 (UE4) 中,碰撞检测是一个重要的功能,用于判断物体之间是否发生接触或重叠。为了实现有效的碰撞检测,正确配置对象的碰撞属性至关重要。 #### 配置静态网格体(Static Mesh) 对于静态网格体而言,在其细节面板中存在多个碰撞有关的选择项: - **Collision Presets**: 提供预设好的碰撞模式,如无碰撞、阻挡所有、仅触发器等。选择合适的预设能够快速设定基本行为[^1]。 - **Custom Collision Response to Channels**: 自定义响应通道允许开发者指定当特定类型的实体接近该物件时应如何反应——忽略(Ignore),阻挡(Block),还是作为触发(Overlap)。 #### 动态角色及其他可移动Actor 针对动态角色或者其他可能改变位置的对象来说,则需关注以下几个方面来调整碰撞特性: - **Character Movement Component Settings**: 如果涉及到人物模型的话,那么在其运动组件内也有专门用来控制地面检测以及楼梯攀爬等功能的相关参数调节空间[^2]。 - **Simulation Generates Hit Events**: 对于那些启用物理模拟的角色或其他物体,此选项决定了它们是否会因为其他物体相碰而产生命中事件(Hit Event)。这有助于捕捉并处理碰撞瞬间的信息变化[^3]。 #### 编辑蓝图中的碰撞形状 除了上述提到的内容外,还可以通过编辑蓝图里的`Simple Collision Shape`来自由设计更精确复杂的碰撞边界。例如将默认盒形改为球形或者胶囊状以适应不同需求下的交互逻辑[^4]。 ```cpp // C++代码示例:为Actor添加简单的球形碰撞体积 USphereComponent* SphereComp; SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereCollider")); RootComponent = SphereComp; // 设置半径大小 SphereComp->SetSphereRadius(50.f); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值