栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

UE4 c++命令模式浅析(回撤的思路引导与引擎命令操作查找)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

UE4 c++命令模式浅析(回撤的思路引导与引擎命令操作查找)

UE4 c++命令模式浅析(回撤的思路引导)

前言:
在游戏开发的过程中,我们不乏对应设计模式的运用,甚至有些人看到一些源码,知道这是干嘛的,却依旧不知道他这使用的“美妙”所在。这时候,你就需要深入抓设计模式了,它其中的涉及到的深度,令人着迷。例如:在游戏开发过程中,我们有些时候会对游戏的过程中错误的操作进行回撤,这时候为什么不考虑一下命令模式。想必再次之前,大家已经熟悉命令模式了吧,当然,熟不熟悉作为一篇博文,还是要描述的。
命令模式,简而言之就是将命令封装成一个类,像AI对Pawn发送命令一样,存在收发者。由于命令模式其解耦性质,所以在游戏开发模式中运用很广泛,当然,你要是了解过UE的自定义命令,这时候,你也会知道,UE这种规模庞大的引擎,都会选择用其作为底层框架搭建。因此,虚幻在处理回撤时候就会很方便。
平常我们写简单的输入测试时候,可能会这样处理:

void InputHandler::handleInput()
{
  if (isPressed(BUTTON_Space)) jump();
  else if (isPressed(BUTTON_G)) fireGun();
  else if (isPressed(BUTTON_F)) PickUp();
  else if (isPressed(BUTTON_W)) MoveTo();
}

这样处理,就将你的输入耦合到其中,不妨猜想,你是准备每帧的处理。接着,我们简单的用命令模式来进行操作:

class Command
{
public:
  virtual ~Command() {}
  virtual void execute(AActor& actor) = 0;
};

我们可以再execute中处理自己的方法

class InputHandle
{
public:
	Command* BUTTON_Space;
	Command* BUTTON_G;
	Command* BUTTON_F;
	Command* BUTTON_W;
};
Command* InputHandler::handleInput()
{
  if (isPressed(BUTTON_Space)) return  BUTTON_Space;
  else if (isPressed(BUTTON_G)) return  BUTTON_G;
  else if (isPressed(BUTTON_F))  return  BUTTON_F;
  else if (isPressed(BUTTON_W))  return  BUTTON_F;
  return NULL;
}

这时候,我们只需要返回对应的命令,为空的时候,则表示没有命令。如果采用这种方式,我们在处理Crtl+Z这种回撤操作时候,也会更简单,这时候,我们只需要在Command类中添加一个UnDo的方法,里面写好属于自己的回撤方法就行。

class Command
{
public:
  virtual ~Command() {}
  virtual void execute(AActor& actor) = 0;
  virtual void undo(AActor& actor) = 0;
};

我们可以记录每条执行的命令,当需要消息回撤时候,我们可以调用存储命令的栈,然后调用其中的undo方法,已达到回撤的目的,当然,这种回撤操作设置最大存储命令,什么时候需要清理命令,就根据你们自身的项目处理即可。

接下来让我们来看看UE中对命令的处理方式:

在我们检索Command之后,发现在配置文件有一大堆类似这种格式的配置文件,却不知道它是干嘛的,但是仔细一阅读,不妨让我们看出了一些端倪,我们这这时候回去猜想,虚幻引擎也是用命令模式搭建的,因此,它的这些不同按键去绑定了一行行的命令。想到这,想必你也有思路继续向下检索。

假设我们要对拖拽物体时候按End物体会放在地面上并贴合这种操作感兴趣,仔细想想,这个是在编辑器时候的命令,那我们不妨检索一下EditorCommand,这时候你会发现一个类叫FPListEditorCommands,它的注释也告诉你这是绑定编辑器按键的类。

class FPListEditorCommands : public TCommands
{
public:

	
	FPListEditorCommands() 
		: TCommands("PListEditor", NSLOCTEXT("PListEditor", "PListEditor", "PList Editor"), NAME_None, FEditorStyle::GetStyleSetName())
	{
	}

	
	TSharedPtr NewCommand;
	
	TSharedPtr OpenCommand;
	
	TSharedPtr SaveCommand;
	
	TSharedPtr SaveAsCommand;
	
	TSharedPtr DeleteSelectedCommand;
	
	TSharedPtr MoveUpCommand;
	
	TSharedPtr MoveDownCommand;
	
	TSharedPtr AddDictionaryCommand;
	
	TSharedPtr AddArrayCommand;
	
	TSharedPtr AddStringCommand;
	
	TSharedPtr AddBooleanCommand;

	
	virtual void RegisterCommands() override
	{
		UI_COMMAND( NewCommand, "New", "Creates a new plist file", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::N) );
		UI_COMMAND( OpenCommand, "Open", "Opens an existing plist file", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::O) );
		UI_COMMAND( SaveCommand, "Save", "Saves the current plist file", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::S) );
		UI_COMMAND( SaveAsCommand, "Save As", "Saves the current plist file to a specific location", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Alt, EKeys::S) );
		UI_COMMAND( DeleteSelectedCommand, "Remove Selected", "Removed the selected entries from the plist", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::R) );
		UI_COMMAND( MoveUpCommand, "Move Up", "Moves the selected entry up within its parent", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::U) );
		UI_COMMAND( MoveDownCommand, "Move Down", "Moves the selected entry down within its parent", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::D) );
		UI_COMMAND( AddDictionaryCommand, "Add Dictionary", "Adds a new dictionary to the selected file or array", EUserInterfaceActionType::Button, FInputChord());
		UI_COMMAND( AddArrayCommand, "Add Array", "Adds a new array to the selected file, array, or dictionary", EUserInterfaceActionType::Button, FInputChord());
		UI_COMMAND( AddStringCommand, "Add String", "Adds a new string to the selected file, array, or dictionary", EUserInterfaceActionType::Button, FInputChord());
		UI_COMMAND( AddBooleanCommand, "Add Boolean", "Adds a new boolean to the selected file, array, or dictionary", EUserInterfaceActionType::Button, FInputChord());
	}
};

这时候,我们可以看到平常一些按键所绑定的快捷键

引入眼帘的快捷键的绑定FInputChord(EModifierKey::Control, EKeys::D)),仔细想想,我们的那个键是End键,那么我们不妨检索EKeys::End试试。

这时候,我们发现对应的End按键的命令还是有好些个,让我们简单的看看。

这就有我们平常跳开启模拟时候跳到最后一帧的操作
在我们大致翻动的过程中发现了这条命令:

UI_COMMAND( SnapToFloor, "Snap to Floor", "Snaps the actor or component to the floor below it", EUserInterfaceActionType::Button, FInputChord( EKeys::End ) );

这句话Snaps the actor or component to the floor below it翻译过来,不就是将将Actor或组件捕捉到其下方的地面,不就是我们想要的找操作吗,虽然命令找到了,可是有些时候我们想要对应的方法咋办,别急,接着往下看:

我们接着检索这个命令的名字,发现大致还是有好几处在使用:

简单看看第一行:

这不就是我们平常绑定的方法的写法吗,接着看下去,切进SnapToFloor_Clicked,我们发现了这层还不是我们想要的

void FLevelEditorActionCallbacks::SnapToFloor_Clicked( bool InAlign, bool InUseLineTrace, bool InUseBounds, bool InUsePivot )
{
	const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "SnapActorsToFloor", "Snap Actors To Floor") );
	SnapTo_Clicked( InAlign, InUseLineTrace, InUseBounds, InUsePivot );
}

继续看SnapTo_Clicked中:

void FLevelEditorActionCallbacks::SnapTo_Clicked( const bool InAlign, const bool InUseLineTrace, const bool InUseBounds, const bool InUsePivot, AActor* InDestination )
{
	// Fires ULevel::LevelDirtiedEvent when falling out of scope.
	FScopedLevelDirtied		LevelDirtyCallback;

	bool bSnappedComponents = false;

	// Let the component visualizers try to handle the selection.
	if (GUnrealEd->ComponentVisManager.HandleSnapTo(InAlign, InUseLineTrace, InUseBounds, InUsePivot, InDestination))
	{
		bSnappedComponents = true;
	}

	// Gather the selected actors and components to later add to the ingore list for object snapping
	TArray ObjectsToIgnore;
	for (FSelectedEditableComponentIterator It(GEditor->GetSelectedEditableComponentIterator()); It; ++It)
	{
		USceneComponent* SceneComponent = Cast(*It);
		ObjectsToIgnore.Add(FActorOrComponent(SceneComponent));
	}
	for (FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It)
	{
		AActor* Actor = Cast(*It);
		ObjectsToIgnore.Add(FActorOrComponent(Actor));
	}


	if( !bSnappedComponents && GEditor->GetSelectedComponentCount() > 0 )
	{
		for(FSelectedEditableComponentIterator It(GEditor->GetSelectedEditableComponentIterator()); It; ++It)
		{
			USceneComponent* SceneComponent = Cast(*It);
			if(SceneComponent)
			{
				SceneComponent->Modify();
				AActor* ActorOwner = SceneComponent->GetOwner();
				bSnappedComponents = true;
				if(ActorOwner)
				{
					ActorOwner->Modify();
					GEditor->SnapObjectTo(FActorOrComponent(SceneComponent), InAlign, InUseLineTrace, InUseBounds, InUsePivot, FActorOrComponent(InDestination), ObjectsToIgnore);
					ActorOwner->InvalidateLightingCache();
					ActorOwner->UpdateComponentTransforms();

					LevelDirtyCallback.Request();
				}
			}
		}

		USceneComponent* LastComp = GEditor->GetSelectedComponents()->GetBottom();

		GEditor->SetPivot(LastComp->GetComponentLocation(), false, true);
	}

	if( !bSnappedComponents )
	{
		for(FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It)
		{
			AActor* Actor = Cast(*It);
			if(Actor)
			{
				Actor->Modify();
				GEditor->SnapObjectTo(FActorOrComponent(Actor), InAlign, InUseLineTrace, InUseBounds, InUsePivot, FActorOrComponent(InDestination), ObjectsToIgnore);
				Actor->InvalidateLightingCache();
				Actor->UpdateComponentTransforms();

				LevelDirtyCallback.Request();
			}
		}


		AActor* Actor = GEditor->GetSelectedActors()->GetBottom();
		if(Actor)
		{
			GEditor->SetPivot(Actor->GetActorLocation(), false, true);

			if(UActorGroupingUtils::IsGroupingActive())
			{
				// set group pivot for the root-most group
				AGroupActor* ActorGroupRoot = AGroupActor::GetRootForActor(Actor, true, true);
				if(ActorGroupRoot)
				{
					ActorGroupRoot->CenterGroupLocation();
				}
			}
		}
	}

	GEditor->RedrawLevelEditingViewports();
}

我们终于找到了按End键执行的操作了,到此,想必大家对应UE的命令也稍微熟悉很多了吧,当我们创建插件时候窗口插件时候,UE也会自动给我们生成对应的Command的类,到此,文章不再继续深入,感兴趣的小伙伴,可以自己大致看看。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/954506.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号