跳到主内容
Documentation

1.使用代码来配置材质.md

2025/12/16预计阅读 10 分钟

定义属性


	// 预览材质 - 可放置(绿色)
	UPROPERTY(EditDefaultsOnly, Category = "Trap Placement")
	UMaterialInterface* ValidPlacementMaterial;

	// 预览材质 - 不可放置(红色)
	UPROPERTY(EditDefaultsOnly, Category = "Trap Placement")
	UMaterialInterface* InvalidPlacementMaterial;

构造函数中初始化



AMyBasePlayerController::AMyBasePlayerController()
{
	static ConstructorHelpers::FObjectFinder<UMaterialInstance> ValidMaterialFinder(
		TEXT(
			"/Script/Engine.MaterialInstanceConstant'/Game/Material/Trap/M_TrapPreview_Valid_Inst.M_TrapPreview_Valid_Inst'")
	);
	if (ValidMaterialFinder.Succeeded())
	{
		ValidPlacementMaterial = ValidMaterialFinder.Object;
		UE_LOG(LogTemp, Log, TEXT("✅ 成功加载有效放置材质"));
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("❌ 加载有效放置材质失败"));
	}

	static ConstructorHelpers::FObjectFinder<UMaterialInstance> InvalidMaterialFinder(
		TEXT(
			"/Script/Engine.MaterialInstanceConstant'/Game/Material/Trap/M_TrapPreview_Invalid_Inst.M_TrapPreview_Invalid_Inst'")
	);
	if (InvalidMaterialFinder.Succeeded())
	{
		InvalidPlacementMaterial = InvalidMaterialFinder.Object;
		UE_LOG(LogTemp, Log, TEXT("✅ 成功加载无效放置材质"));
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("❌ 加载无效放置材质失败"));
	}
}

下面是来自 AI的解释

没问题!这段代码是 Unreal Engine 中非常标准的**“生成预览物体(Ghost Actor)”**的流程。常用于建造系统,让玩家在点击放置之前,看到一个半透明的虚影。

下面我为你逐行添加了详细的注释,并在代码下方附带了核心知识点的深度解析。

详细注释版代码


/**
 * 函数功能:创建用于预览的陷阱Actor(虚影)
 * 参数 TrapClass:想要预览的陷阱的类类型(蓝图类或C++类)
 */
void AMyBasePlayerController::CreatePreviewActor(TSubclassOf<AMyTrapBase> TrapClass)
{
    // ====================================================
    // 1. 清理阶段:防止同时存在多个预览物体
    // ====================================================
    
    // 检查当前是否已经存在一个预览Actor
    if (PreviewTrapActor)
    {
        // 如果存在,必须先销毁它,否则切换道具时旧的虚影会残留在原地
        PreviewTrapActor->Destroy();
        // 将指针置空,防止悬空指针(Dangling Pointer)导致崩溃
        PreviewTrapActor = nullptr;
    }

    // ====================================================
    // 2. 校验阶段
    // ====================================================

    // 如果传入的类是空的(例如玩家取消了手持物品),直接返回,不执行后续逻辑
    if (!TrapClass)
    {
        return;
    }

    // ====================================================
    // 3. 生成配置阶段
    // ====================================================

    // 定义生成参数结构体,用于控制 SpawnActor 的行为
    FActorSpawnParameters SpawnParams;
    
    // 核心设置:始终生成,忽略碰撞冲突。
    // 默认情况下,如果生成位置有东西挡住,UE可能会禁止生成。
    // 但作为预览虚影,我们需要它即使在墙里也能显示出来,所以设为 AlwaysSpawn。
    SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    // 设置为 true 表示生成过程不应该失败(在某些边缘情况下保证稳定性)
    SpawnParams.bNoFail = true;

    // ====================================================
    // 4. 实例化阶段
    // ====================================================

    // 在世界中生成 Actor。
    // FVector::ZeroVector 和 FRotator::ZeroRotator 是临时位置/旋转。
    // 因为在 Tick 函数中,我们会根据射线检测每帧更新它的位置,所以这里初始值不重要。
    PreviewTrapActor = GetWorld()->SpawnActor<AMyTrapBase>(
        TrapClass,              // 要生成的类
        FVector::ZeroVector,    // 初始位置 (0,0,0)
        FRotator::ZeroRotator,  // 初始旋转 (0,0,0)
        SpawnParams             // 上面配置的参数
    );

    // ====================================================
    // 5. 初始化设置阶段(关键:把实体变成“虚影”)
    // ====================================================

    if (PreviewTrapActor)
    {
        // --- 逻辑层设置 ---

        // 禁用 Actor 层级的碰撞。
        // 预览物体绝不能阻挡玩家移动,也不能触发子弹打击或物理模拟。
        PreviewTrapActor->SetActorEnableCollision(false);

        // 禁用 Tick(每帧运行)。
        // 真正的陷阱可能有复杂的逻辑(如索敌),预览时不需要运行这些逻辑,节省性能。
        PreviewTrapActor->SetActorTickEnabled(false);

        // --- 视觉层设置 ---

        // 获取该 Actor 身上所有的静态网格体组件(即物体的模型部分)
        TArray<UStaticMeshComponent*> MeshComponents;
        PreviewTrapActor->GetComponents<UStaticMeshComponent>(MeshComponents);

        // 遍历每一个网格体组件进行设置
        for (UStaticMeshComponent* MeshComp : MeshComponents)
        {
            if (MeshComp)
            {
                // 彻底关闭组件的物理碰撞(双重保险,防止物理引擎计算它)
                MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);

                // 关闭阴影投射。
                // 预览虚影通常是发光的或者半透明的,投射阴影会显得很怪且浪费性能。
                MeshComp->SetCastShadow(false);

                // --- 材质透明度处理 ---
                
                // 遍历模型上的每一个材质槽(Material Slot)
                for (int32 i = 0; i < MeshComp->GetNumMaterials(); ++i)
                {
                    // 创建“动态材质实例” (MID - Material Instance Dynamic)。
                    // 只有创建了 MID,我们才能在游戏运行时修改材质参数(如颜色、透明度)。
                    if (UMaterialInstanceDynamic* DynMat = MeshComp->CreateDynamicMaterialInstance(i))
                    {
                        // 修改名为 "Opacity" 的标量参数,设为 0.5(半透明)。
                        // 【注意】:这要求你的材质(Material)中必须有一个名为 "Opacity" 的参数,
                        // 且材质的混合模式(Blend Mode)必须设为 Translucent(半透明),否则这行代码无效。
                        DynMat->SetScalarParameterValue(FName("Opacity"), 0.5f);
                    }
                }
            }
        }

        // --- 状态管理 ---

        // 初始设为隐藏。
        // 这样做是为了防止 Actor 刚生成在 (0,0,0) 时闪烁一下。
        // 等到下一次 Tick 检测到射线击中点并设置好正确位置后,再将其设为显示。
        PreviewTrapActor->SetActorHiddenInGame(true);

        // 记录当前正在预览的类型,用于后续逻辑判断(比如判断玩家是否换了武器)
        CurrentPreviewTrapClass = TrapClass;

        // 打印日志,方便调试(仅在 Output Log 中显示)
        UE_LOG(LogTemp, Log, TEXT("创建陷阱预览Actor: %s"), *TrapClass->GetName());
    }
}

💡 核心知识点总结(学习重点)

1. ESpawnActorCollisionHandlingMethod::AlwaysSpawn

这是建造系统中最关键的设置之一。

  • 如果不加这行:如果你对着墙角或者非常狭窄的地方尝试生成预览,UE 会检测到新生成的 Actor 会和墙壁重叠,于是拒绝生成,导致 PreviewTrapActor 为空。
  • 加上这行:强制生成,哪怕它穿模了。对于“虚影”来说,穿模是完全可以接受的。

2. CreateDynamicMaterialInstance (MID)

这是让物体变色、变透明的核心技术。

  • 静态材质:游戏运行时不能改。
  • 动态材质实例 (MID):是静态材质的克隆体,可以在运行时随意修改参数。
  • 重要前提:你的材质蓝图(Material Graph)里必须真的连入了一个叫 "Opacity" 的 Scalar Parameter 节点,并且材质本身支持半透明,这行代码 SetScalarParameterValue 才会生效。

3. 性能优化:SetActorTickEnabled(false)

这是一个良好的编程习惯。

  • 假设你的 AMyTrapBase 里有一个 Tick 函数用来检测敌人。
  • 当你只是想预览放置位置时,不需要这些检测逻辑运行。
  • 禁用 Tick 可以避免不必要的计算开销,特别是在低配机器上。

4. PreviewTrapActor->Destroy() 的时机

在函数开头销毁旧 Actor 是为了实现**“单例预览”**。

  • 如果不销毁:玩家每按一次切换武器,场景里就会多一个没人管的虚影,最后内存溢出。
  • 正确的逻辑是:旧的不去,新的不来。

5. SetActorHiddenInGame(true) 的作用

这是一种视觉上的“防抖动”处理。

  • Actor 生成是在 (0,0,0)(通常是地图中心地下)。
  • 如果不先隐藏,玩家可能会在瞬间看到地图中心闪过一个物体,然后它才跳到玩家准星位置。
  • 先隐藏,等位置计算好了(在另一个 Update 函数里)再显示,体验最丝滑。
返回顶部