UE4 – 学习笔记之五 (总结二三四)

Posted in Unity3D&UE4

虚幻4的Engine模块 为了方便查看类关系,您可以点击这里在新标签中查看大图: 点我查看大图 Gameplay框架: Actor是一个游戏玩法实体,通常包含一个或多个Component,在游戏过程中可以静态地放置在Level中或通过Spawned动态生成,并支持多人游戏的网络复制。所有的Actor都从AActor类延伸出来,这个类是可重构游戏对象的基类。 需要注意的是,AActor有一个子类AInfo(不进行渲染而只进行信息的存储),之所以继承于Actor的原因大概是想利用Actor已经实现好的各种基本功能,尤其是网络复制,尽管AInfo不进行渲染,但很多也需要进行网络同步的 从某种意义上讲,Actor可以被认为是容纳特殊类型对象(称为组件)的容器。例如,一个CameraActor包含一个CameraComponent。但是这不是绝对的,功能也能通过自己的Actor对象的代码实现,但这明显不是一个最好的解决方法,因为要考虑重用性的问题,这样,各种各样的功能组件可以被用在各种的Actor上,如下图将CameraComponent附加到Pawn上 Pawn类那些可以由玩家或AI控制的所有Actor的是基础类(也继承于Actor)。Pawn是World范围内玩家或AI实体的物理表征。这不仅意味着Pawn决定了玩家或AI实体在视觉上的外观,还意味着它在碰撞和其他物理交互方面与世界的交互方式。Pawn代表游戏中玩家或实体的物理位置,旋转等。一个Character是一种特殊类型的Pawn,它有人型的走动,动画等功能。 它们之间的使用差别:如果游戏的主角们是各种各样的球体的,而玩家只能控制球体的滚动,这个时候只需使用Pawn就行,但如果游戏是一个写实的风格,需要一个人形的角色,直接使用Character会很省事,它已经集成了很多人形角色的功能和组件。 以下是官方的区别解释: 默认情况下,Controller和Pawns 之间有一对一的关系,即每个控制器在任何给定时间只控制一个Pawn,但是也能设置为一对多。此外,游戏过程中Spawned的Pawn不会被Controller自动Possess,需要自己增加逻辑调用Controller的Possess函数。 主要有两种子Controller: PlayerController和AIController PlayerController是由玩家进行控制的,AIController则是电脑计算的控制器 以下是Controller的官方描述 为了网络游戏中其它Player信息的传递,还有一个Info类:Player State PlayerState是为服务器上的每个玩家(或在独立游戏中)创建的。玩家状态被复制到所有客户端,并且包含关于玩家的网络游戏相关信息,例如玩家名称,分数等。 如下图所示的那样:PlayerState是被Controller所包含的,但是需要注意:一般AIController是没有自己可用的PlayerState的。 以上就是Pawn,Controller和PlayerState之间的联系 以下是PlayerState的官方解释: 接下来是游戏模式和游戏状态的类,顺带提及GameSession 有两个主要类处理关于正在玩的游戏的信息:Game Mode和Game State 即使是最开放的游戏也有规则的基础,这些规则组成了Game Mode(只在服务器上,一般只存放不变的量)。在最基本的层面上,这些规则包括: 参加游戏的Player和观众的数量,以及Player和观众的最大数量。 玩家如何进入游戏,其中可能包括用于选择spawn位置和其他spawn/respawn行为的规则。 是否可以暂停游戏,以及如何暂停游戏。 关卡之间的转换,包括游戏是否应以电影模式开始。 总的来说GameModeBase定义正在玩的游戏。它管理着游戏规则,得分,在这种游戏类型中允许哪些演员存在,以及谁可以进入游戏。 它只在服务器上实例化,并不会在客户端上存在。 在C ++ UGameEngine :: LoadMap()中为游戏玩法初始化关卡时,GameModeBase actor被实例化 。 此GameMode actor的类由在World Settings中设置的GameMode Override值或游戏的Project Settings中设置的DefaultGameMode条目确定。 这个类的主要内容即是管理游戏规则,defalut玩家Pawn的管理,玩家加入游戏的行为和初始化(HUD)。与GameSession不同,GameModeBase是上层的规则,它没有具体到Player的操作,GameSession则是一个具体的一次会话实例 比如:它的InitNewPlayer(): 1.在session上注册player GameSession->RegisterPlayer(NewPlayerController, UniqueId.GetUniqueNetId(), UGameplayStatics::HasOption(Options, TEXT(“bIsFromInvite”))); 2.Find a….

UE4 – 学习笔记之四

Posted in Unity3D&UE4

为了方便查看类关系,您可以点击这里在新标签中查看大图: 点我查看大图 AGameModeBase: GameModeBase定义正在玩的游戏。它管理着游戏规则,得分,在这种游戏类型中允许哪些演员存在,以及谁可以进入游戏。 它只在服务器上实例化,并不会在客户端上存在。 在C ++ UGameEngine :: LoadMap()中为游戏玩法初始化关卡时,GameModeBase actor被实例化 。 此GameMode actor的类由(按顺序)URL?game = xxx,并在World Settings中设置的GameMode Override值或游戏的Project Settings中设置的DefaultGameMode条目确定。 这个类的主要内容即是管理游戏规则,defalut玩家Pawn的管理,玩家加入游戏的行为和初始化(HUD)。与GameSession不同,GameModeBase是上层的规则,它没有具体到Player的操作,GameSession则是一个具体的一次会话实例 方法: InitNewPlayer(): 在session上注册player     GameSession->RegisterPlayer(NewPlayerController, UniqueId.GetUniqueNetId(), UGameplayStatics::HasOption(Options, TEXT(“bIsFromInvite”))); Find a starting spot,找到起始点 Set up spectating,设置观战(如果是观众) NewPlayerController->StartSpectatingOnly(); Init player’s name NewPlayerController->PlayerState->SetPlayerName(); InitGame(): 通过World创建了GameSession,这里说明一点:UWorld::SpawnActor在Create新Actor后会将其纳入指定的Ulevel之下(根据SpawnParameters参数,默认在CurrentLevel) AActor* const Actor = NewObject<AActor>(LevelToSpawnIn, Class, NewActorName, SpawnParameters.ObjectFlags, Template);     LevelToSpawnIn->Actors.Add( Actor );     LevelToSpawnIn->ActorsForGC.Add(Actor); 而且SpawnActor只在World中,而不在level中,更不在Actor中,原因是Actor是存在一个Outer的这里一般是Level(大概也能是一个Component),Level中有Actor的直接索引,World则是通过Levels的遍历访问Actor的,因此New Actor不必在World中注册 InitGameState():….

UE4 – 学习笔记之三

Posted in Unity3D&UE4

这次在二的基础上分析了UWorld,ULocalPlayer,ULevel的部分成员 先上总体关系图 为了方便查看类关系,您可以点击这里在新标签中查看大图: 点我查看大图 以下是具体的分析 UWorld类: World是代表地图或沙箱的顶级对象,Actors和Components 将存在并被渲染。 一个世界可以是一个单一的Persistent Level,其后可选地跟着一个存着 streaming levels 的list,可以通过volumes and blueprint功能加载和卸载,或者可以是由世界组织组成的层次集合。 在独立的游戏中,除了在目标和当前世界同时存在的无缝区域转换期间,通常只存在一个World。在编辑器中存在许多World:正在编辑的关卡,每个PIE实例,每个具有交互式渲染视口的编辑器工具等等。 ActiveGroupActors: 当前活跃的Actor数组 AudioDeviceHandle: 处理这个World的有源音频设备 AudioVolumes: 控制Volumes内部和外部的Audio效果的Tset 相关知识:更多参考 Volumes:它游戏中不可见,仿佛类似一个包围盒并有overlap的触发函数,但它是一个独立的Actor,可以直接放在Level中,而且不止是overlap的功能,它也可以acting as a collision surface,从而阻止玩家进入Volumes。 它有很多子类 bAggressiveLOD: 加强LOD效果,当帧数低于DesiredFrameRate太多时 bBegunPlay: 是否Actor的BeginPlay()已经被调用 bDropDetail: 去除高细节的Actor,当帧数低于DesiredFrameRate太多时 bIsDefaultLevel: 是否默认的Level bIsTearingDown: World是否处于销毁状态 bPlayersOnly: 当世界tick时,只更新玩家,+Pending则延迟一帧后执行 Layers: World中Actor引用的所有图层列表 bPostTickComponentUpdate: 表示在世界Tick期间,我们执行”脏”组件的最终组件更新(在PostAsyncWork和效果物理场景已运行之后) MyParticleEventManager: 粒子事件管理器 NetworkManager: 网络管理器 PersistentLevel: 持久的Level:包含世界信息,默认画笔和在游戏过程中产生的Actor 一开始就加载进World StreamingLevels: 后续动态加载进World,也可设置成alwaysloaded或者使用BP自定义加载时机 Levels: World中所有的Level索引 TickGroup: 当前的Tick组….

UE4 – 学习笔记之二

Posted in Unity3D&UE4

这是本节学习的整体框架,图中只列出了一部分关键函数和少量的相关解释   为了方便查看类关系,您可以点击这里在新标签中查看大图: 点我查看大图 【您也可以在下方查看大图和编辑它】 接下来将依次分析AActor,APawn和AController   相关知识: 虚幻类的命名前缀 虚幻引擎提供了在构建过程中为您生成代码的工具。这些工具有一些类命名的期望,如果名称与预期不符,将会触发警告或错误。下面的类前缀列表描述了这些工具所期待的。 从Actor(Class AActor)派生的前缀要为A,例如AController。 从Object(Class UObject)派生的前缀要为U,例如UComponent。 Enums类以为E前缀,例如EFortificationType。 Interface 类通常以I为前缀,例如IAbilitySystemInterface。 Template 类以T为前缀,例如TArray。 从SWidget (Slate UI)派生的前缀要为S,例如SButton。 别的一切都以F为前缀,例如FVector。 Engine/GameFramework/Actor: AAcotr: Actor是可以在关卡中放置或产生的Object的基类。 Actor可能包含一系列ActorComponents,它们可以用来控制actor如何移动,如何渲染等等。Actor的另一个主要功能是在播放过程中通过网络复制属性和函数调用。 AutoReceiveInput属性:决定哪个player控制,其中TEnumAsByte是以类型安全的方式将枚举值存储为字节的模板,实际上作用就是在enum之上封装了安全性的一层,具体的类说明。     相关知识: 看到上面有UPROPERTY(EditAnywhere, Category=Input) 这里是UE4的反射机制:注册这个属性到反射系统,使对其可见,即是在编辑器中显示这个属性,并能够对该属性进行其他管理,比如垃圾回收(GC),【它根据root set(Singleton,管理着所有有效的索引)来进行垃圾回收操作的,通过UPROPERTY()宏使root set保存有这个属性的引用,每次GC时就不会将其视为垃圾处理】 EditAnywhere参数说明表示该属性可以通过属性窗口,原型和实例进行编辑,并指定所在的Category为Input,也就是说可以在UE4编辑器中这个Actor的详细信息中的名为”Input“的类目中找到这个属性。 引用官方说明:反射是程序在运行时检查自身的能力。这非常有用,是虚幻引擎的基础技术,为编辑器,序列化,垃圾收集,网络复制和Blueprint / C ++通信等许多系统提供动力。然而,C ++本身并不支持任何形式的反射,所以虚幻拥有自己的系统来收集,查询和处理关于C ++类,结构体,函数,成员变量和枚举的信息。我们通常将反射称为属性系统,因为反射也是图形术语。 类似的还有: UCLASS() – 用于告诉Unreal为一个类生成反射数据。该类必须来自UObject。 USTRUCT() – 用于告诉Unreal为结构生成反射数据。 GENERATED_BODY() – UE4将其替换为为该类型生成的所有必需的样板代码。 UPROPERTY() – 使用UCLASS或USTRUCT的成员变量作为UPROPERTY。UPROPERTY有很多用途。它可以允许变量被复制,序列化和从蓝图访问。它们也被垃圾收集器用来跟踪UObject的引用数量。 UFUNCTION() – 启用UCLASS或USTRUCT的类方法作为UFUNCTION。UFUNCTION可以允许从Blueprints中调用类方法,并用作RPC等等。 它们的说明符表如下: UCLASS说明符列表 UPROPERTY说明符列表 UFUNCTION指示符列表 USTRUCT说明符列表 是否允许此Actor在收到BeginPlay事件之前进行tick的开始。….

UE4 – 学习笔记之一

Posted in Unity3D&UE4

先贴上原文:使用 C++类向导 创建 LightSwitchCodeOnly 类 学习使用中完全有问题:原因可能是文档太旧,更新频繁也可能UE4官方并不重视C++ 不过蓝图的确方便,但毕竟C++才是本质,还是有必要了解的 不多说废话了…下面是我遇到的问题以及相对的解决方法【我使用的是4.18.3】 首先,当我已经完成了仅使用蓝图完成这个可开关灯的类时,已经迫不及待想要尝试使用C++完成这个任务 第一次使用发现这个编码的确与众不同(与Unity3D相比) 比如 UCLASS() 这样的宏…等等,这被称为反射机制,与之相关的更多的资料请参考此文章结尾的参考 以上即为官方文档中的说明。【注意:在一切问题的开始一定要先检查拼写错误…血的教训】 也许你的Visual Studio并不能好好地工作,你可以参考官方的建议: 为虚幻 4 设置 Visual Studio 当然,也有XCode的相关配置指导。 但是即使我跟着指导完成了设置,似乎并没有我想象中的那样完美. 问题一: #include “Components/PointLightComponent.h” #include “Components/SphereComponent.h” 没错,我早该想到的:也许是这个原因,这个组件没有被include 这就是与它们相关的种种错误的根源 class UPointLightComponent* PointLight1; class USphereComponent* Sphere1; 请将它们添加到LightSwitchCodeOnly.h中 问题二: LightSwitchCodeOnly.h中: UFUNCTION() //void OnOverlapBegin(class AActor* otherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool BFromSweep, const FHitResult& SweepResult); void OnOverlapBegin(class UPrimitiveComponent* HitComp,class AActor*….

Unity3D – 一个小小的游戏

Posted in Unity3D&UE4

这是我的第一个Unity3D的游戏,下载地址:REME 另外还有一个低配网页WebGL版本:点我跳转 没有错,它十分地简陋,没有开始结束画面~ 你只需要控制雷姆移动并且挥动手中的流星锤击倒怪物即可,可能你有幸一次性全部击杀完毕,那么恭喜你完成了游戏,此时地图上只有孤单的雷姆和手中的流星锤,不过似乎这个概率不算高,因为一旦碰到怪物,画面上可能只剩下孤单滚动的锤锤和一脸懵比的怪物…没有重新开始功能,没有重新开始功能,没有重新开始功能,重要的事情说三遍!当然我并不满足于现状,学习路途漫漫,以后会回来填坑的,记住这个时刻【2018年3月11日14:38:06】 下面来说说这个游戏的结构吧: 这个流星锤可没少折腾我,一定要把球的碰撞检测初始位置放到地面的上方!如果开始位置就嵌入了地面,那么他的运动轨迹将出乎你的意料。。。惨痛的教训 游戏背景音乐就不多说了。 上代码: RemeControl.cs: using System.Collections; using System.Collections.Generic; using UnityEngine; public class RemeControl : MonoBehaviour { //一些参数 public float speed=9f; public float rotspeed=10f; private float movez = 0; private float movex = -0.00001f; private bool dead = false; //起初自作聪明以为自己实现一个输入缓存能够减少卡顿,结果事实证明这是画蛇添足 // private float []rot=new float[2]; // private int buff; // Use….