2020-01-12 16:41:18 a497406594 阅读数 9
  • 从这里开始虚幻4-Editor介绍 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2510 人正在学习 去看看 杨石兴

前言

首先,C++不同于C#或者Java,没有自带反射机制,只能通过C++一些传统艺能变着法地实现。

有哪些反射

1、类对象反射:简单地说就是程序运行时读进来一个字符串,然后程序就会自动创建出字符串对应名字的类对象

2、类属性反射:能够遍历得到类的所有成员,知道它们的数据类型和名字。最实际的例子就是Unreal和Unity中UI的实现,当定义一个类成员变量时,它就能在UI中显示出来。而不需要费事地再去针对新增的变量写UI代码,让它显示在界面上。

3、函数反射:能够自动搜集函数的相关的所有信息,包括名称,参数个数,参数类型,返回值。

类对象反射

其实用工厂模式就能轻松解决这个问题。解决这个问题的思路是

1、首先,创建对象的接口要方便,最好统一成一个,也就是说类似下面这个接口。无论什么对象,只要传个名字就能返回对象指针。

static Object* CreateObject(std::string className)

2、由于返回的是同样类型的指针,那么就必须继承相同的基类,也就是Object。

3、然后要怎么根据名字创建对象呢?就需要用到工厂模式了,用一个map存储工厂类,然后工厂类去创建对象

class ObjectFactory
{
public:
	virtual Object* CreateObject() = 0;
};

template<class T>
class ObjectFactoryImp :public ObjectFactory
{
public:
	virtual Object* CreateObject()
	{
		return new T();
	}
};

std::map<std::string, ObjectFactory*> factoryMap;

4、也就是说我们需要去构造factoryMap,当调用CreateObject时,实际是调用factoryMap[className]->CreateObject(); 构造的方法也很简单,也就是每个类定义的时候,也进行一次初始化,初始化要做的事情就是注册一个工厂类。

template<class T>
void RegisterObject(std::string className)
{
	ObjectFactory* factory = new ObjectFactoryImp<T>();
	if (factoryMap.count(className) == 0)
	{
		factoryMap[className] = factory;
	}
    else
    {
	    delete factory;
    }
}

// 使用方式
RegisterObject<类名>(类名对应字符串);

5、以上就是类反射实现的所有内容,用法也很简单,先调用RegisterObject去注册对象,然后直接调用CreateObject创建对象就行了。我在此基础上做了简单的封装,感兴趣的可以下载看看https://download.csdn.net/download/a497406594/12099078

 

类属性反射

https://zhuanlan.zhihu.com/p/88144082 这篇文章有提到怎么实现,个人是比较倾向于静态反射的,不过老实说用了太多语法糖就不香了,可读性较差。个人是比较推荐这本书的作者用的方式,知识点在第八章。https://zhuanlan.zhihu.com/p/54166550。这部分比较难,并且它这个牵扯的比较多,因为这本书的作者还想顺便实现UI的反射。

如果只是想实现属性反射的话,我按照自己的理解整理了下,至少需要做以下工作

1、定义一个属性类,至少包含属性数据类型,名字,在类中的偏移

2、要实现属性反射的类中需要定义一个静态成员队列,用来存储属性对象,每个属性对应队列里一个元素。这个队列记录在另外的一个单例类里,因为涉及继承,以及是静态的,所以不可能放自己的类里。最好是用一个通用的类,类似上面类反射一样放在全局里,或者某个静态类或单例里。

3、需要像类对象反射一样,先对每个属性进行注册,也就是不得不.h声明完变量之后,又在cpp里再写一遍类似声明的东西。所谓注册,就是创建属性对象加到类的队列里。这一步可以用宏包装下,可以减少一定的代码量

我简单实现了下,感兴趣的可以下载看看https://download.csdn.net/download/a497406594/12100744

函数反射

https://zhuanlan.zhihu.com/p/54166550 这本书依旧有这个知识点,我还不会就先Mark一下吧

2015-02-25 17:29:08 zhuyingqingfen 阅读数 1797
  • 从这里开始虚幻4-Editor介绍 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2510 人正在学习 去看看 杨石兴


完成代码 见 

 http://download.csdn.net/detail/zhuyingqingfen/8457091

1. 设计模式中抽象工厂的泛型 实现

2. c++ 自动生成模板代码 的例子 具体实现见:c++ 泛型编程 之 自动生成代码




////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design 
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any 
//     purpose is hereby granted without fee, provided that the above copyright 
//     notice appear in all copies and that both that copyright notice and this 
//     permission notice appear in supporting documentation.
// The author or Addison-Wesley Longman make no representations about the 
//     suitability of this software for any purpose. It is provided "as is" 
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////
#ifndef LOKI_ABSTRACTFACTORY_INC_
#define LOKI_ABSTRACTFACTORY_INC_

// $Id: AbstractFactory.h 771 2006-10-27 18:05:03Z clitte_bbt $



#include "HierarchyGenerators.h"
#include "typelists.h"
#include <cassert>
#include <iostream>

namespace Loki
{

////////////////////////////////////////////////////////////////////////////////
//Type2Type<T> 用于消除歧义,因为同一个继承体会有多个AbstractFactoryUnit的具现体
//如:AbstractFactoryUnit<A> ,AbstractFactoryUnit<B>等
//DoCreate这个函数返回的并不是一个常类型,因为T为可变的
//在c++中,你可以"返回型别为Pointer to Derived class "的函数改写(overide)为“返回
// pointer to base class"的函数。这个就是”协变式返回型别(covariant return types)
	/*
	class A{
	public:
	virtual A * ff() = 0;
	};
	class B:public A{
	public:
	B * ff(){return this;}
	};
	使用:
	B b;
	*/

////////////////////////////////////////////////////////////////////////////////

    template <class T>
    class AbstractFactoryUnit
    {
    public:
        virtual T* DoCreate(Type2Type<T>) = 0;
        virtual ~AbstractFactoryUnit() {} 
    };

////////////////////////////////////////////////////////////////////////////////
// class template AbstractFactory
// Defines an Abstract Factory interface starting from a typelist
////////////////////////////////////////////////////////////////////////////////

    template
    <
        class TList,
        template <class> class Unit = AbstractFactoryUnit
    >
    class AbstractFactory : public GenScatterHierarchy<TList, Unit>
    {
    public:
        typedef TList ProductList;
        
        template <class T> T* Create()
        {
            Unit<T>& unit = *this;
            return unit.DoCreate(Type2Type<T>());
        }
    };
    
////////////////////////////////////////////////////////////////////////////////
// class template OpNewFactoryUnit
// Creates an object by invoking the new operator
////////////////////////////////////////////////////////////////////////////////

    template <class ConcreteProduct, class Base>
    class OpNewFactoryUnit : public Base
    {
        typedef typename Base::ProductList BaseProductList;  
    protected:
        typedef typename BaseProductList::Tail ProductList;
    public:
        typedef typename BaseProductList::Head AbstractProduct;
        ConcreteProduct* DoCreate(Type2Type<AbstractProduct>)
        {//AbstractProduct抽象产品,ConcreteProduct具体产品,同Soldier和SillySoldier的关系
			std::cout<<"基类:"<<typeid(Base).name()<<std::endl;
			std::cout<<"基类产品:"<<typeid(BaseProductList).name()<<std::endl;
			std::cout<<"抽象名称:"<<typeid(AbstractProduct).name()<<std::endl;
			std::cout<<"具体实现:"<<typeid(ConcreteProduct).name()<<std::endl;
			std::cout<<"剩余的:"<<typeid(ProductList).name()<<std::endl;
            return new ConcreteProduct;
        }
    };	 

////////////////////////////////////////////////////////////////////////////////
// AbstractFact:提供的是“欲实现的abstract factory接口”,并隐式提供了一组产品(products)
// Creator: 是个Policy,规定如何实际生成对象。
// TList: 提供了“这个factory所要生成的concrete classes”的集合。
//		   这个typelist中的每一个具象型别都有一个abstractFactory的typelist中
//         具有相同索引的抽象型别。
// 使用Reverse的原因:
/*
	OpNewFactoryUnit 生成重载函数DoCreate是从上到下的顺序生成的(体现在ProductList上,因为继承类都是从父类
	 找到Head,然后再把Tail传递给下面的类。如果BaseProductList是Soldier, Monster, SuperMonster,那么
	 子类的AbstractProduct是Soldier,ProductList是Monster, SuperMonster,再往下继承AbstractProduct是Monster
	 ProductList是SuperMonster,而ConcreteFactory 把引数TList 传递给OpNewFactoryUnit中的ConcreteProduct引数
	 是从下到上的,如果是ConcreteFactory中的TList是SillySoldier, SillyMonster, SillySuperMonster,那么
	 ConcreteFactory的继承关系树中从下到上OpNewFactoryUnit的ConcreteProduct依次是SillySoldier,SillyMonster,SillySuperMonster
	 这样正好和 DoCreate(Type2Type<AbstractProduct>)中的AbstractProduct的顺序相反,所以需要Reverse一下

	 当然也可以修改OpNewFactoryUnit 而不翻转ConcreteFactory中的TList 修改成如下:
	 typedef typename Base::ProductList BaseProductList;  
	 protected:
	 typedef typename Reverse<typename Reverse<BaseProductList>::Result::Tail>::Result ProductList;
	 public:
	 typedef typename Reverse<BaseProductList>::Result::Head AbstractProduct;
	 不过这种设计不太好,因为每个Creator都要处理这种翻转情况,不如在ConcreteFactory类中一下彻底解决问题好。


*/
////////////////////////////////////////////////////////////////////////////////

    template
    <
        class AbstractFact,
        template <class, class> class Creator = OpNewFactoryUnit,
        class TList = typename AbstractFact::ProductList
    >
	class ConcreteFactory
		: public GenLinearHierarchy<typename Reverse<TList>::Result, Creator, AbstractFact>
    {
    public:
		typedef typename AbstractFact::ProductList ProductList;
		typedef TList ConcreteProductList;
    };

} // namespace Loki


#endif // end file guardian

class Soldier { public: virtual ~Soldier() {} };
class Monster { public: virtual ~Monster() {} };
class SuperMonster { public: virtual ~SuperMonster() {} };

class SillySoldier : public Soldier {};
class SillyMonster : public Monster {};
class SillySuperMonster : public SuperMonster {};

class BadSoldier : public Soldier {};
class BadMonster : public Monster {};
class BadSuperMonster : public SuperMonster {};


void abstractfactory_test()
{
	using namespace Loki;

	typedef AbstractFactory<TYPELIST_3(Soldier, Monster, SuperMonster)> AbstractEnemyFactory;

	typedef ConcreteFactory<AbstractEnemyFactory, OpNewFactoryUnit,
		TYPELIST_3(SillySoldier, SillyMonster, SillySuperMonster)> EasyLevelEnemyFactory;
	typedef ConcreteFactory<AbstractEnemyFactory, OpNewFactoryUnit,
		TYPELIST_3(BadSoldier, BadMonster, BadSuperMonster)> HardLevelEnemyFactory;
	
 
	std::auto_ptr<AbstractEnemyFactory> easyFactory(new EasyLevelEnemyFactory);
	std::auto_ptr<AbstractEnemyFactory> hardFactory(new HardLevelEnemyFactory);

	
	//1
	Monster *s;
	s = easyFactory->Create<Monster>();
	delete s;
	//2
 
	AbstractFactoryUnit<Soldier> & soldier_V = *easyFactory;
	AbstractFactoryUnit<Monster> & monster_V = *easyFactory;
	AbstractFactoryUnit<SuperMonster> & superMonster_V = *easyFactory;
 
}


下图 为 两个类 的继承关系图






2016-02-29 12:38:41 wag2765 阅读数 446
  • 从这里开始虚幻4-Editor介绍 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2510 人正在学习 去看看 杨石兴

下面两个例子都是基于蓝图的使用,不是C++

 

Unreal Engine 4 Spline and SplineMesh Components

https://www.youtube.com/watch?v=MqPeFIEJUmg

 

UE4 Tutorial: Add spline-meshes procedurally

https://www.youtube.com/watch?v=7YUxM0NDWRY

2016-05-26 10:33:18 wag2765 阅读数 247
  • 从这里开始虚幻4-Editor介绍 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2510 人正在学习 去看看 杨石兴

相关文章:

如何用蓝图实现Delegate Event:

http://aigo.iteye.com/blog/2269663

 

原文作者:@玄冬Wong

转载请注明出处:http://aigo.iteye.com/blog/2301010

 

虽然官方doc上说Event的Binding方式跟Multi-Cast用法完全一样,Multi-Cast论坛上也有很多例子,但是实际是不一样。。而且论坛上找不到相关例子。后来看了下runtime/core的源码,Event的binging方式如下(附上完整步骤):

 

1,先定义自定义Event,此例中我们定义在GameMode中,名字叫 MyInitEvent。例子中定义的是无参函数,如果需要带参数,官方doc讲的有

UCLASS()
class MYPROJ_API AMyGameMode : public AGameMode
{
	GENERATED_BODY()
	
public:

	AHGameMode();

	//定义Event
	DECLARE_EVENT(MyUObject, MyInitEvent)
	
	//用于获取Event引用的函数,方便在GameMode之外执行binding
	MyInitEvent& OnInitialize() { return InitEvent; }
	
private:
	//Event实例化
	MyInitEvent InitEvent;
	
	//Event的binding指针
	FDelegateHandle DHandle;
}

 

2,执行Binding。MyUObject::TestFun是需要被触发的函数,这里假设是一个普通的无参成员函数:

MyUObject* MyObj = NULL;	//这里假设创建MyUObject
if (MyObj)
{
	DHandle = MyGameMode->OnInitialize().AddUObject(MyObj, &MyUObject::TestFun);
}

 

注:任何UObject的普通函数都可以被设置为Event的回调函数,但如果函数是UFUNCTION,那么需要将AddUObject替换为AddUFunction

 

3,触发Event,这里假设在BeginPlay中触发:

void AMyGameMode::BeginPlay()
{
	InitEvent.Broadcast();
}

 

4,移除Event,比如之前bingding的event不想再触发了:

MyGameMode->OnInitialize().Remove(DHandle);

 

Event 和 Delegate 区别

Delegate 只能绑定一个回调函数,Delegate执行Execute()函数时,只会触发事先绑定的一个函数;Event可以绑定任意个函数,一旦执行Event的Broadcast()函数,所有回调函数按Add顺序依次执行。

Dynamic Multicast Delegate也可以同时绑定多个回调函数,但是其运行效率要比 Event 慢。

 

Event 和 Delegate 共同点

回调函数都不能有返回值。

 

Dynamic Delegate 与 常规 Delegate 区别

Dynamic Delegates 可以被序列化:即他们的注册函数可以通过名称查找获取,相比普通Delegate,速度要慢。

2017-03-20 13:44:52 panda1234lee 阅读数 2375
  • 从这里开始虚幻4-Editor介绍 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2510 人正在学习 去看看 杨石兴


3. 创建带参数的委托

我们可以通过修改委托的签名来使其接受参数

比如我们需要接受一个参数的话,可以在 GameMode 中这样声明:

DECLARE_DELEGATE_OneParam(FParamDelegateSignature, FLinearColor)

注意:这个宏与之前稍有不同,后缀多出了一个 _OneParam ,而且我们还需要指定接受参数的类型——本例为 FLinearColor


接着再添加一个 FParamDelegateSignature 成员 
FParamDelegateSignature MyParameterDelegate;	

这和之前一样,创建一个委托实例作为 GameMode 成员


然后创建一个 Actor 类,取名为 ParamDelegateListener,

在头文件中添加以下声明

UFUNCTION()
void SetLightColor(FLinearColor LightColor);

UPROPERTY()
UPointLightComponent* PointLight;

ParamDelegateListener.cpp 

#include "Test.h"
#include "UE4TestGameMode.h"
#include "ParamDelegateListener.h"


// Sets default values
AParamDelegateListener::AParamDelegateListener()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");
	RootComponent = PointLight;

}

// Called when the game starts or when spawned
void AParamDelegateListener::BeginPlay()
{
	Super::BeginPlay();
	UWorld* TheWorld = GetWorld();
	if (TheWorld != nullptr)
	{
		AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
		AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);
		if (MyGameMode != nullptr)
		{
			// Binds a UObject-based member function delegate. UObject delegates keep a weak reference to your object. You can use ExecuteIfBound() to call them.(注意绑定的还是 UFUNCTION)
			MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor);
		}
	}

}

// Called every frame
void AParamDelegateListener::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}
// 1个参数
void AParamDelegateListener::SetLightColor(FLinearColor LightColor)
{
	PointLight->SetLightColor(LightColor);
}

回到 MyTriggerVollume.cpp,在 NotifyActorBeginOverlap 函数中添加以下代码:

MyGameMode->MyParameterDelegate.ExecuteIfBound(FLinearColor(1, 0, 0, 1));	// 带一个参数

与之前不同的是,我们需要多指定一个参数,参数类型和我们之前的委托声明一致。


显然,MyTriggerVolume 压根就无需知道 ParamDelegateListener 的存在,却通过 GameMode 就可以调用 ParamDelegateListener 的函数了,很大程度上降低了类间的耦合度。


解绑委托方式与之前相同,不再赘述。


4.通过委托绑定传递负载数据(Payload Data)

稍加修改,我们就可以在委托被调用时传递额外创建时的参数(additional creation-time parameter),即我们在 MyTriggerVolume 中的调用方式不变,仍然是 ExecuteIfBound(FLinearColor(1, 0, 0, 1)),但可以额外添加一些负载数据,在 ParamDelegateListener 中的 BindUObject 上添加。


首先修改 AParamDelegateListener::BeginPlay 中的 BindUObject,为其添加一个 bool 负载数据

MyGameMode->MyParameterDelegate.BindUObject(this, &AParamDelegateListener::SetLightColor, false);

并修改 SetLightColor 的定义

UFUNCTION()
void SetLightColor(FLinearColor LightColor, bool EnableLight);

// 2个参数
void AParamDelegateListener::SetLightColor(FLinearColor LightColor, bool EnableLight)
{
	PointLight->SetLightColor(LightColor);
	PointLight->SetVisibility(EnableLight);
}

注意:负载数据并不局限于带参数的委托,其他的委托形式也可以使用


5. 多播委托(Multicast Delegate)

之前说的委托,都是只绑定了一个函数指针,而多播委托绑定的是一个函数指针集合,每个函数指针都有对应的一个委托句柄,当广播(Broadcast)委托的时候,他们将会被激活。


首先在 GameMode 中添加多播的委托声明

需要明确声明为多播

DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)

接着在类中声明一个 FMulticastDelegateSignature 成员

FMulticastDelegateSignature MyMulticastDelegate;	

其次,创建一个新 Actor 类,命名为 MulticastDelegateListener

在其头文件中添加以下声明:

FDelegateHandle MyDelegateHandle;

UPROPERTY()
UPointLightComponent* PointLight;

UFUNCTION()
void ToggleLight();

virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

大部分和之前的 Listener 类很相似,但是多一个 委托句柄实例,将用它来存储委托实例的引用,我们的添加(AddUObject)和移除(Remove)都需要它作为参数


 源文件的代码如下:

// Sets default values
AMulticastDelegateListener::AMulticastDelegateListener()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	PointLight = CreateDefaultSubobject<UPointLightComponent>("PointLight");
	RootComponent = PointLight;

}

// Called when the game starts or when spawned
void AMulticastDelegateListener::BeginPlay()
{
	Super::BeginPlay();
	UWorld* TheWorld = GetWorld();
	if (TheWorld != nullptr)
	{
		AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
		AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);
		if (MyGameMode != nullptr)
		{
			// Adds a UObject-based member function delegate. UObject delegates keep a weak reference to your object.
			// 注册一个对象方法
			MyDelegateHandle = MyGameMode->MyMulticastDelegate.AddUObject(this, &AMulticastDelegateListener::ToggleLight);
		}
	}

}

// Called every frame
void AMulticastDelegateListener::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}

void AMulticastDelegateListener::ToggleLight()
{
	PointLight->ToggleVisibility();
}

void AMulticastDelegateListener::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
	UWorld* TheWorld = GetWorld();
	if (TheWorld != nullptr)
	{
		AGameMode* GameMode = Cast<AGameMode>(UGameplayStatics::GetGameMode(TheWorld));
		AUE4TestGameMode * MyGameMode = Cast<AUE4TestGameMode>(GameMode);
		if (MyGameMode != nullptr)
		{
			// Removes a function from this multi-cast delegate's invocation list (performance is O(N)). Note that the order of the delegates may not be preserved!
			MyGameMode->MyMulticastDelegate.Remove(MyDelegateHandle);
		}
	}
}

MyTriggerVolume.cpp 的实现为:

// Broadcasts this delegate to all bound objects, except to those that may have expired.
MyGameMode->MyMulticastDelegate.Broadcast();

广播函数很像我们之前的 ExecuteIfBound函数,但有一点不同,它不需要检查是否有函数绑定在委托上。

最后的效果是,如果我们往场景中拖放了四五个MulticastDelegateListener,当我们进入触发区域,它们的灯会同时打开或关闭,因为每个实例函数都被添加到委托集合当中;

如果拖放了四五个DelegateListener 到场景中,当我们进入触发区域,只有最后一个拖进场景的灯会亮,这是因为委托只绑定了最后一个实例函数。


(未完待续)


js_WebGL 游戏

阅读数 8

没有更多推荐了,返回首页