Published on

绘制图形(非debug)

Authors
  • avatar
    Name
    东哥
    Twitter

蓝图方法DrawDebugLine(以及其他图形)的方法只能在开发者模式中使用

下文为了得到发布版的DrawLine之类的功能

LineBatchComponent

  • 在UWorld中看到如下声明
/** Line Batchers. All lines to be drawn in the world. */
	UPROPERTY(Transient)
	class ULineBatchComponent*					LineBatcher;

	/** Persistent Line Batchers. They don't get flushed every frame.  */
	UPROPERTY(Transient)
	class ULineBatchComponent*					PersistentLineBatcher;

	/** Foreground Line Batchers. This can't be Persistent.  */
	UPROPERTY(Transient)
	class ULineBatchComponent*					ForegroundLineBatcher;
  • 然后创建并注册组件
*****
if(!LineBatcher)
		{
			LineBatcher = `NewObject<ULineBatchComponent>`();
			LineBatcher->bCalculateAccurateBounds = false;
		}

		if(!LineBatcher->IsRegistered())
		{	
			LineBatcher->RegisterComponentWithWorld(this);
		}
****
  • 再到组件LineBatchComponent.h

  • 先关注组件内部申明的类FLineBatcherSceneProxy : public FPrimitiveSceneProxy

    • 这个代理继承自FPrimitiveSceneProxy,这个类可以得到重要的FPrimitiveDrawInterface,通过这个类可以画出需要的图形(其他各类方法基本都是通过PDI来画出图形),代码如下
//重写的方法,不需要自己调用
void FLineBatcherSceneProxy::GetDynamicMeshElements(const `TArray<const FSceneView*>`& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
{
	QUICK_SCOPE_CYCLE_COUNTER( STAT_LineBatcherSceneProxy_GetDynamicMeshElements );

	for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
	{
		if (VisibilityMap & (1 << ViewIndex))
		{const FSceneView* View = Views[ViewIndex];
         //得到PDI
			FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);

			for (int32 i = 0; i < Lines.Num(); i++)
			{
                //通过PDI画线,Lines是`TArray<FBatchedLine>`私有成员
				PDI->DrawLine(Lines[i].Start, Lines[i].End, Lines[i].Color, Lines[i].DepthPriority, Lines[i].Thickness);
			}

			for (int32 i = 0; i < Points.Num(); i++)
			{
                //同理画点
				PDI->DrawPoint(Points[i].Position, Points[i].Color, Points[i].PointSize, Points[i].DepthPriority);
			}
  • 代理构造的时候提供ULineBatchComponent
  • 目的是把ULineBatchComponent的数据传给此代理
FLineBatcherSceneProxy::FLineBatcherSceneProxy(const ULineBatchComponent* InComponent) :
	FPrimitiveSceneProxy(InComponent), Lines(InComponent->BatchedLines), 
	Points(InComponent->BatchedPoints), Meshes(InComponent->BatchedMeshes)
{
	bWillEverBeLit = false;
}
  • 下面到ULineBatchComponent
  • ::DrawLine方法把FBatchedLine添加到数组,然后通过Tick递减时间并删除
void ULineBatchComponent::DrawLine(const FVector& Start, const FVector& End, const FLinearColor& Color, uint8 DepthPriority, const float Thickness, const float LifeTime)
{
    //构造一个FBatchedLine成员
	new(BatchedLines) FBatchedLine(Start, End, Color, LifeTime, Thickness, DepthPriority);

	// LineBatcher and PersistentLineBatcher components will be updated at the end of UWorld::Tick
	MarkRenderStateDirty();
}
void ULineBatchComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
	bool bDirty = false;
	// Update the life time of batched lines, removing the lines which have expired.
	for(int32 LineIndex=0; LineIndex < BatchedLines.Num(); LineIndex++)
	{
		FBatchedLine& Line = BatchedLines[LineIndex];
		if (Line.RemainingLifeTime > 0.0f)
		{
			Line.RemainingLifeTime -= DeltaTime;
			if(Line.RemainingLifeTime <= 0.0f)
			{
				// The line has expired, remove it.
				BatchedLines.RemoveAtSwap(LineIndex--);
				bDirty = true;
			}
		}
	}
    *************************

DrawDebug版本

通过world得到相应的LineBatcher然后调用DrawLine方法

if (GEngine->GetNetMode(InWorld) != NM_DedicatedServer)
	{
		// this means foreground lines can't be persistent 
		ULineBatchComponent* const LineBatcher = GetDebugLineBatcher( InWorld, bPersistentLines, LifeTime, (DepthPriority == SDPG_Foreground) );
		if(LineBatcher != NULL)
		{
			float const LineLifeTime = GetDebugLineLifeTime(LineBatcher, LifeTime, bPersistentLines);
			LineBatcher->DrawLine(LineStart, LineEnd, Color, DepthPriority, Thickness, LineLifeTime);
		

封装蓝图函数库

FlibDrawGraphics.h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "FlibDrawGraphics.generated.h"

/**
 * 
 */
class ULineBatchComponent;
UCLASS()
class GWORLD_API UFlibDrawGraphics : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
private:
	static ULineBatchComponent* GetDebugLineBatcher(const UWorld* InWorld, bool bPersistentLines, float LifeTime, bool bDepthIsForeground);

public:
	UFUNCTION(BlueprintCallable)
		static void G_DrawLine(UObject* context, FVector Start, FVector End, FLinearColor Color, uint8 DepthPriority, float Thickness, float Duration, bool bPersistent = false);
	UFUNCTION(BlueprintCallable)
		static void G_DrawBox(UObject* context, FVector  Center, FVector  Box, FLinearColor Color, uint8 DepthPriority, float Thickness, float Duration, bool bPersistent = false);
	UFUNCTION(BlueprintCallable, meta = (context = "WorldContextObject"))
		static void G_DrawSoildBox(UObject* context, FVector  Center, FVector  Extent, FLinearColor  Color, uint8 DepthPriority, float Duration, bool bPersistent = false);


};

FlibDrawGraphics.cpp
#include "FlibDrawGraphics.h"
#include "Components/LineBatchComponent.h"
#include "Engine/World.h"

ULineBatchComponent* ULibGameFunc::GetDebugLineBatcher(const UWorld* InWorld, bool bPersistentLines, float LifeTime, bool bDepthIsForeground)
{

	return (InWorld ? (bDepthIsForeground ? InWorld->ForegroundLineBatcher : ((bPersistentLines || (LifeTime > 0.f)) ? InWorld->PersistentLineBatcher : InWorld->LineBatcher)) : NULL);

}


//画线
void ULibGameFunc::BP_DrawLine(UObject* context, FVector Start, FVector End, FLinearColor Color, uint8 DepthPriority, float Thickness, float Duration, bool bPersistent /*= false*/)
{
	UWorld* world = context->GetWorld();
	if (world)
	{
		//通过优先级得到对应的组件
		ULineBatchComponent* const LineBatcher = GetDebugLineBatcher(world, bPersistent, Duration, DepthPriority == SDPG_Foreground);
        //如果是永久的设置时间-1
		float const ActualLifetime = bPersistent ? -1.0f : ((Duration > 0.f) ? Duration : LineBatcher->DefaultLifeTime);
		LineBatcher->DrawLine(Start, End, Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
	}
}


//画盒子边框,通过8个点然后DrawLine得到
void ULibGameFunc::BP_DrawBox(UObject* context, FVector Center, FVector Box, FLinearColor Color, uint8 DepthPriority, float Thickness, float Duration, bool bPersistent /*= false*/)
{

	UWorld* world = context->GetWorld();
	if (world)
	{

		ULineBatchComponent* const LineBatcher = GetDebugLineBatcher(world, bPersistent, Duration, DepthPriority == SDPG_Foreground);
		float const ActualLifetime = bPersistent ? -1.0f : ((Duration > 0.f) ? Duration : LineBatcher->DefaultLifeTime);
		if (LineBatcher)
		{		LineBatcher->DrawLine(Center + FVector(Box.X, Box.Y, Box.Z), Center + FVector(Box.X, -Box.Y, Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(Box.X, -Box.Y, Box.Z), Center + FVector(-Box.X, -Box.Y, Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(-Box.X, -Box.Y, Box.Z), Center + FVector(-Box.X, Box.Y, Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(-Box.X, Box.Y, Box.Z), Center + FVector(Box.X, Box.Y, Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(Box.X, Box.Y, -Box.Z), Center + FVector(Box.X, -Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(Box.X, -Box.Y, -Box.Z), Center + FVector(-Box.X, -Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(-Box.X, -Box.Y, -Box.Z), Center + FVector(-Box.X, Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(-Box.X, Box.Y, -Box.Z), Center + FVector(Box.X, Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(Box.X, Box.Y, Box.Z), Center + FVector(Box.X, Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(Box.X, -Box.Y, Box.Z), Center + FVector(Box.X, -Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(-Box.X, -Box.Y, Box.Z), Center + FVector(-Box.X, -Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);
			LineBatcher->DrawLine(Center + FVector(-Box.X, Box.Y, Box.Z), Center + FVector(-Box.X, Box.Y, -Box.Z), Color.ToFColor(true), DepthPriority, Thickness, ActualLifetime);

		}
	}

}

//画实体的盒子
void ULibGameFunc::DrawSoildBox(UObject* context, FVector Center, FVector Extent, FLinearColor Color, uint8 DepthPriority, float Duration, bool bPersistent/*=false*/)
{
	FBox Box = FBox::BuildAABB(Center, Extent);

	if (context)
	{
		UWorld* World = context->GetWorld();
		if (World)
		{
			
			ULineBatchComponent* const LineBatcher = GetDebugLineBatcher(World, bPersistent, Duration, DepthPriority == SDPG_Foreground);
			if (LineBatcher != NULL)
			{

				float const ActualLifetime = bPersistent ? -1.0f : ((Duration > 0.f) ? Duration : LineBatcher->DefaultLifeTime);
				LineBatcher->DrawSolidBox(Box, FTransform::Identity, Color.ToFColor(true), DepthPriority, ActualLifetime);
			}
		}

	}
}

蓝图测试