Based UE_Project Notes
设置版本控制
虚幻打包
将下面3个文件删除
打包之前先热重载并运行一下
打包到一个空文件夹中
Bookmarks
书签功能:标记特定位置,快速跳转到之前标记的位置
- 添加书签:选择你想要标记的位置,然后使用快捷键(Ctrl + 1到Ctrl + 9)来添加书签
- 跳转到书签:使用相应的快捷键(1到9)可以快速跳转到已设置的书签位置
- 管理书签:在视角中,可以找到书签管理器,允许查看和删除现有书签。
显示帧率
快捷键:Ctr+Shift+H
游戏视角
快捷键:G
全屏模式
快捷键:F11
高分辨率截图
落地快捷键
快捷键:End
光照
DirectionalLight(定向光源):类似平行光
- 可以通过设置Rotation来调整光源
- 将定向光照设置为moveable
- 可以最多使用两个定向光源,为每个定向光源设置大气太阳光照指数(Atmosphere Sun Light Index)
例如,0表示太阳,1代表月亮 - 使用 Ctrl + L 并移动鼠标将调整已设为指数0的定向光源。它通常是太阳
- 使用 Ctrl + L + Shift 并移动鼠标将调整已设为指数1的定向光源。它通常是月亮
定向光源可以打开温度选项设置太阳的温度
开启丁达尔效应:
SkyAtmosphere(天空大气):蓝天
VolumetricCloud(体积云):云彩效果
SkyLight(天空光照):阴影光照
- 将天空光照设置为moveable
- 实时捕捉进行动态一天中的时间模拟
ExponentialHeightFog(指数级高度雾):雾
地形
创建地形时可以选择大小,不要过大
快捷键:Shift+1,Shift+2...
注意:若地形无法显示,打开'World Partition'****左键选择或自定义框取区域,右键选择分区加载地图
- Sculpt:
鼠标左键: 升高地形
鼠标左键 + Shift: 降低地形
材质
-
添加材质:
-
通过Bridge获取材质:
-
将材质设置为rough使得材质不会反光
-
LandscapeLayerBlend:图层混合
添加6个元素并命名:
将Bridge中获得的材质的第一个纹理拖到M_Landscape材质中并连接BaseColor:
将第二个纹理以相同的顺序拖到M_Landscape材质中并连接Normal:
将材质添加到Landscape中
地形图层混合:使用Paint来绘制,使用Weight-Blended Layer (normal)允许多个层的叠加,能够实现精细的渐变效果,用于创建复杂的地形和表面的自然过渡,Non Weight-Blended Layer类似于雪覆盖在地上
更改材质颜色:
绘制地形:
绘制草木
选择Foliage Mode里的Paint来绘制
动态小草:开启Wind
垂直的树木:关闭Align to Normal
设置树木碰撞:
提高帧率
PostProcessVolume(后期处理体积)
- 为整个场景或特定区域应用各种视觉效果
- 开启全地图包含:
- 调节色温:
- 调节Bloom(光晕):
- 调节最大最小光照:
- 调节饱和、对比、伽马值等
打包关卡演员
将多个元素合并为一个演员
创建一个Level:可以将资产里的Level导入
将Level中的资产分离,便于操作
可以从官方案例里添加Actor到Mylevel中
设置纹理采样源
使纹理加载更快捷
启用虚拟纹理支持
设置Visual Studio
【002 Setting up Visual Studio】
https://www.bilibili.com/video/BV1Hi22YZEqt?vd_source=78f7f9f8d415f8df9a79d831bb4f277c
虚幻中的继承关系
创建C++类
创建蓝图类
新建两个文件夹,Blueprints和Items用于存放蓝图
蓝图中打印字符串
- Print String:在屏幕和日志中均显示
- Log String:仅在日志中显示
- Duration:屏幕字符显示持续时间
- 先打印的字符串在下面:
- Key值:两个Print String节点使用相同的Key值时,后一个节点的消息会替换前一个节点的消息
只打印了第二条消息
- 在Tick中打印时:
- 在Tick中打印并设置Key值时:后一个值会替换前一个值
因为Delta Time为字符串,不会发生变化,所以屏幕上的值不变
将Delta Seconds打印到屏幕可以发现屏幕上的值在变化
C++中打印字符串
①UE_LOG():将字符输出到日志
Log , Warning , Error:
UE_LOG(LogTemp, Log, TEXT("BeginPlay called!"));
UE_LOG(LogTemp, Warning, TEXT("BeginPlay called!"));
UE_LOG(LogTemp, Error, TEXT("BeginPlay called!"));
在TEXT中使用%s,%d:
FString name = "Sam";
UE_LOG(LogTemp, Log, TEXT("Name:%s"), *name);
UE_LOG(LogTemp, Log, TEXT("%d"),10);
②GEngine->AddOnScreenDebugMessage():将字符打印到屏幕
函数原型:
使用实例:
if(GEngine)
{
GEngine->AddOnScreenDebugMessage(1, 60.f, FColor::Blue, FString("Print to the screen"));
}
if(GEngine)
{
FString Message = FString::Printf(TEXT("DeltaTime:%f"), DeltaTime);
GEngine->AddOnScreenDebugMessage(2, 30.f, FColor::Cyan, Message);
}
GetName()
获取当前物体名称
if(GEngine)
{
FString Name = GetName();
FString Message = FString::Printf(TEXT("Name:%s"),*Name);
GEngine->AddOnScreenDebugMessage(2, 30.f, FColor::Cyan, Message);
}
GetActorLocation()
获取当前actor的位置
FVector Location = GetActorLocation();
ToString()
将FVector,FRotator类转为字符串
FVector Location = GetActorLocation();
UE_LOG(LogTemp, Warning, TEXT("Location:%s"), *Location.ToString());
DrawDebugSphere()
绘制调试球体
函数原型:
void DrawDebugSphere(
UWorld* World,
const FVector& Center,
float Radius,
int32 Segments,
FColor Color,
bool bPersistentLines,
float LifeTime,
uint8 DepthPriority,
float Thickness
);
使用实例:
DrawDebugSphere(GetWorld(), GetActorLocation(), 20.f, 40, FColor::Red, false, 30.f);
GetActorForwardVector()
这个向量是单位向量,表示 actor 的前方在世界坐标系中的方向
FVector Forward = GetActorForwardVector();
DrawDebugLine()
绘制调试线条
函数原型:
void DrawDebugLine(
UWorld* World,
const FVector& Start,
const FVector& End,
FColor Color,
bool bPersistentLines,
float LifeTime,
uint8 DepthPriority,//深度优先级,值越小优先级越高(默认值通常是 0)
float Thickness//绘制线条的厚度
);
函数实例:
//从当前 actor 位置向前延伸 100 单位的红色线条
DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 100, FColor::Red, true);
DrawDebugPoint()
绘制调试点
函数原型:
void DrawDebugPoint(
UWorld* World,
FVector const& Location,
float Size,
FColor Color,
bool bPersistentLines = false,
float Lifespan = 0.0f,
uint8 DepthPriority = 0
);
函数实例:
DrawDebugPoint(GetWorld(), GetActorLocation(), 20, FColor::Blue, false, 60.f);
宏定义
在Source文件夹下的Slash中添加新的头文件DebugMacros
#define DRAW_SPHERE(Location) if(GetWorld()) DrawDebugSphere(GetWorld(),Location,20.f,20,FColor::Red,true);
#define DRAW_LINE(StartLocation,EndLocation) if(GetWorld()) DrawDebugLine(GetWorld(), StartLocation, EndLocation, FColor::Red, true, -1, 0, 1);
#define DRAW_POINT(Location) if(GetWorld()) DrawDebugPoint(GetWorld(), Location, 20, FColor::Blue, false, 60.f);
#define DRAW_VECTOR(StartLocation,EndLocation) if(GetWorld())\
{\
DrawDebugLine(GetWorld(), StartLocation, EndLocation, FColor::Red, true, -1, 0, 1);\
DrawDebugPoint(GetWorld(), EndLocation, 20, FColor::Blue, false, 60.f);\
}
在Item.cpp中包含头文件
DRAW_SPHERE(GetActorLocation());
//DRAW_LINE(GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 100);
//DRAW_POINT(GetActorLocation());
DRAW_VECTOR(GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 100);
SetActorLocation()
设置Actor位置
函数原型:
void SetActorLocation(
const FVector& NewLocation,//要设置的新位置,通常是一个三维向量(X, Y, Z)
bool bSweep = false,//指定是否在移动 Actor 时进行碰撞检测。如果为 true,则会检测移动路径上的任何碰撞,并在碰撞发生时返回一个 FHitResult
FHitResult* OutHit = nullptr, //如果 bSweep 为 true,可以传递一个指向 FHitResult 对象的指针,以获取碰撞信息,包括碰撞的物体、碰撞点等
ETeleportType Teleport = ETeleportType::None//指定移动的类型(如瞬移、逐步移动等)
);
函数实例:
SetActorLocation(FVector(0.f, 0.f, 50.f));
SetActorRotation()
设置Actor旋转
函数原型:
void SetActorRotation(
const FRotator& NewRotation,//要设置的新旋转,通常是一个三维向量(X, Y, Z)
bool bSweep = false, //指定是否在移动 Actor 时进行碰撞检测。如果为 true,则会检测移动路径上的任何碰撞,并在碰撞发生时返回一个 FHitResult
FHitResult* OutHit = nullptr,//如果 bSweep 为 true,可以传递一个指向 FHitResult 对象的指针,以获取碰撞信息,包括碰撞的物体、碰撞点等
ETeleportType Teleport = ETeleportType::None//指定移动的类型(如瞬移、逐步移动等)
);
函数实例:
SetActorRotation(FRotator(0.f, 45.f, 0.f));
Pitch
:沿Y轴旋转
Yaw
:沿Z轴旋转
Roll
:沿X轴旋转
设置固定帧率
AddActorWorldOffset()
通过增加世界坐标偏移量从而进行移动
函数原型:
AddActorWorldOffset(
FVector DeltaLocation,
bool bSweep = false,
FHitResult* Hit = nullptr,
ETeleportType Teleport = ETeleportType::None
);
函数实例:
float MovementRate = 100.f;//速度
AddActorWorldOffset(FVector(0, 0, MovementRate * DeltaTime));//速度*每帧的时间间隔
DrawDebugSphere(GetWorld(), GetActorLocation(), 10, 20, FColor::Red, false, -1);
AddActorWorldRotation()
以世界坐标系的方式,给Actor添加一个旋转增量
函数原型:
void AddActorWorldRotation(
FRotator DeltaRotation,
bool bSweep = false,
FHitResult* Hit = nullptr,
ETeleportType Teleport = ETeleportType::None
);
函数实例:
float RotationRate = 100.f;//速度
AddActorWorldRotation(FRotator(0.f, RotationRate * DeltaTime, 0.f));//速度*每帧的时间间隔
DrawDebugSphere(GetWorld(), GetActorLocation(), 10, 20, FColor::Red, false, -1);
DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 100.f, FColor::Red, false, -1);
三角函数
使Actor以正弦函数上下摆动
RunningTime += DeltaTime;
AddActorWorldOffset(FVector(0.f, 0.f,FMath::Sin(RunningTime*10)*0.4));
UPROPERTY()
- VisibleAnywhere:在默认蓝图和地图内的蓝图实例的详细信息面板中均可见
UPROPERTY(VisibleAnywhere)
int32 num = 10;
- VisibleDefaultOnly:仅在默认蓝图的详细信息面板中可见
UPROPERTY(VisibleDefaultsOnly)
int32 num = 10;
- VisibleInstanceOnly:仅在蓝图实例的详细信息面板中可见
UPROPERTY(VisibleInstanceOnly)
int32 num = 10;
- EditAnyWhere:在默认蓝图和地图内的蓝图实例的详细信息面板中均可编辑
UPROPERTY(EditAnyWhere)
int32 num = 10;
- EditDefaultOnly:仅在默认蓝图的详细信息面板中可编辑
UPROPERTY(EditDefaultOnly)
int32 num = 10;
- EditInstanceOnly:仅在蓝图实例的详细信息面板中可编辑
UPROPERTY(EditInstanceOnly)
int32 num = 10;
- BlueprintReadWrite:在蓝图面板中可'get'和'set'
UPROPERTY(BlueprintReadWrite)
int32 num = 10;
- BlueprintReadOnly:仅在蓝图面板中可'get'
UPROPERTY(BlueprintReadWrite)
int32 num = 10;
注:蓝图属性只能在public中使用,若想在private中使用,需要如下操作
UPROPERTY(BlueprintReadWrite, meta = (AllowPrivateAccess = "true"));
int32 num = 10;
- VisibleAnywhere+BlueprintReadWrite:在默认蓝图和蓝图实例和蓝图面板中均可编辑
UPROPERTY(EditAnywhere,BlueprintReadWrite)//用逗号分隔
int32 num = 10;
- Category:将属性分类
UPROPERTY(EditAnywhere, Category = "MyVarieties")
int32 num1 = 5;
UPROPERTY(EditAnywhere, Category = "MyVarieties")
int32 num2 = 10;
UPROPERTY(EditAnywhere, Category = "MyVarieties|One")
int32 num1 = 5;
UPROPERTY(EditAnywhere, Category = "MyVarieties|One")
int32 num2 = 10;
UPROPERTY(EditAnywhere, Category = "MyVarieties|Two")
int32 num3 = 15;
- Function
BlueprintPure:纯函数
BlueprintCallable:普通函数
UFUNCTION(BlueprintPure)//纯函数
float TransformedSin();
UFUNCTION(BlueprintCallable)//普通函数
float TransformedCosin();
默认蓝图资源:
蓝图实例:
蓝图面板:
模板函数
//.h
template<typename T>
T Avg(T first, T second);
template<typename T>
inline T AItem::Avg(T first, T second)
{
return (first + second) / 2;
}
//.cpp
int32 AvgInt = Avg<int32>(3, 5);
UE_LOG(LogTemp, Warning, TEXT("Average:%d"), AvgInt);
FVector AvgVector = Avg<FVector>(GetActorLocation(), FVector::ZeroVector);
DRAW_POINT(AvgVector);
Component
- UStaticMeshComponent(静态网络体组件):
通常用于显示静态网格,可以将多个StaticMeshComponents组合到同一个Actor中
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* BaseMesh;//静态网络1
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* TurretMesh;//静态网络2
- USceneComponent(场景组件):
包含位置(Location)、旋转(Rotation)和缩放(Scale)属性,可以通过这些属性来定义组件在世界中的位置和方向。可以作为其他组件的父节点,形成一个组件层级结构
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AlloPrivateAccess = "true"))
USceneComponent* ProjectileSpawnPoint;//生成投射物
- USkeletalMeshComponent(骨骼网格组件):
用于显示可以进行骨骼动画的角色和生物
UPROPERTY(VisibleAnywhere)
USkeletalMeshComponent* BirdMesh;
- UCapsuleComponent(碰撞体组件):
用于处理角色和其他物体的碰撞和物理交互
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
UCapsuleComponent* CapsuleComp;
- RootComponent(根组件):
根组件是一个Actor中的基础组件,所有其他组件都是这个组件的子组件。在虚幻引擎中,每个Actor都必须有一个根组件,根组件可以是任何类型的组件(如UStaticMeshComponent、USkeletalMeshComponent、USceneComponent等)
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Collider"));//创建碰撞体组件
RootComponent = CapsuleComp;//将碰撞体组件设置为根组件
- USpringArmComponent(弹簧臂组件):
将相机组件附加到弹簧臂,使其跟随弹簧臂的移动,主要用于实现相机的跟随效果,将弹簧臂的目标设置为任何 Actor,使得相机能够跟随该目标
UPROPERTY(VisibleAnywhere, Category = "Components")
USpringArmComponent* SpringArm;
- UCameraComponent(相机组件):
通常将相机组件附加到 USpringArmComponent,以实现平滑的相机跟随效果
UPROPERTY(VisibleAnywhere, Category = "Components")
UCameraComponent* Camera;
-
创建组件:
T* CreateDefaultSubobject<T>(const FName& SubobjectName);
T:表示要创建的组件的类型,例如 UStaticMeshComponent、UCapsuleComponent等。
SubobjectName:传入一个 FName 类型的名称,用于标识该子对象 -
附加组件:
将一个子组件附加到父组件
void SetupAttachment(父组件名称)
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Collider"));
RootComponent = CapsuleComp;
BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Mesh"));
BaseMesh->SetupAttachment(CapsuleComp);
TurretMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Turret Mesh"));
TurretMesh->SetupAttachment(BaseMesh);
ProjectileSpawnPoint = CreateDefaultSubobject<USceneComponent>(TEXT("Spawn Point"));
ProjectileSpawnPoint->SetupAttachment(TurretMesh);
前向声明
减少头文件包含个数从而减少编译时间
防止循环
用法:
在.h文件中使用class前向声明
在.cpp文件需要使用时包含需要的头文件
Auto Possess Player
在蓝图中设置:
1.当游戏开始时,指定的玩家控制器会自动附加到角色,使得玩家能够立即控制该角色
2.可以设置为自动控制特定的玩家(如 Player 0、Player 1 等),这对于支持多人游戏非常重要
3.Player 0:自动控制第一个玩家(通常是本地玩家)
Player 1:自动控制第二个玩家(适用于多人游戏)
在C++中设置:
AutoPossessPlayer = EAutoReceiveInput::Player0;
设置输入绑定
函数用于绑定玩家输入到角色的功能上
- 设置输入轴:
- 打开 Unreal Engine 编辑器。
- 转到 Edit -> Project Settings。
- 在左侧菜单中选择 Input。
- 在 Axis Mappings 部分,添加一个新的轴映射,命名为 "MoveForward"。
- 为该轴映射添加输入(例如,W/S 键或上/下箭头键),并设置相应的值(例如,W 为 1,S 为 -1)。
- 绑定输入轴:
void MoveForward(float Value);
void Turn(float Value);
void LookUp(float Value);
void ABird::MoveForward(float Value)
{
if(Controller&&Value!=0)
{
AddMovementInput(GetActorForwardVector(), Value);
}
}
void ABird::Turn(float Value)
{
AddControllerYawInput(Value);
}
void ABird::LookUp(float Value)
{
AddControllerPitchInput(Value);
}
void ABird::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)//设置玩家的输入组件
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//BindAxis用于绑定持续的输入事件
//1.轴名称:表示绑定的轴的名称,必须与项目设置中的输入相匹配
//2.对象:指向当前对象的指针
//3.回调函数:将输入轴 "MoveForward" 绑定到 ABird 类的 MoveForward 函数
PlayerInputComponent->BindAxis(FName("MoveForward"), this, &ABird::MoveForward);
PlayerInputComponent->BindAxis(FName("Turn"), this, &ABird::Turn);
PlayerInputComponent->BindAxis(FName("LookUp"), this, &ABird::LookUp);
}
- 在蓝图中添加移动组件:
可以在FloatingPawnMovement中更改Actor速度
- 使用控制器旋转:
- 设置Collision:
弹簧臂附加到相机组件
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
SpringArm->SetupAttachment(RootComponent);
ViewCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("ViewCamera"));
ViewCamera->SetupAttachment(SpringArm);
GameMode
删除BP_Bird
添加Player Start
迁移角色资产
只能添加到Content文件夹
创建Character
创建C++类的Character:
创建蓝图类的Character:
在C++中添加弹簧臂和相机组件
设置骨骼网络资产:
可以自定义材质颜色:
移动到资产页面,复制资产并改名
实现动态修改
取消蓝图控制 控制器的旋转:
在C++中设置控制器旋转为false:
bUseControllerRotationYaw = false;
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
设置绑定轴:
void ASlashCharacter::MoveForward(float Value)
{
if (Controller && Value != 0.f)
{
const FRotator YawRotation(0, GetControlRotation().Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void ASlashCharacter::MoveLeftAndRight(float Value)
{
if (Controller && Value != 0.f)
{
const FRotator YawRotation(0, GetControlRotation().Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
}
void ASlashCharacter::Turn(float Value)
{
AddControllerYawInput(Value);
}
void ASlashCharacter::LookUp(float Value)
{
AddControllerPitchInput(Value);
}
PlayerInputComponent->BindAxis(FName("MoveForward"), this, &ASlashCharacter::MoveForward);
PlayerInputComponent->BindAxis(FName("MoveLeftAndRight"), this, &ASlashCharacter::MoveLeftAndRight);
PlayerInputComponent->BindAxis(FName("Turn"), this, &ASlashCharacter::Turn);
PlayerInputComponent->BindAxis(FName("LookUp"), this, &ASlashCharacter::LookUp);
设置弹簧臂中的Camera Settings:
在蓝图中设置平滑旋转:
设置旋转速度:
在C++中设置平滑旋转:
设置头发组件
【007 Hair and Eyebrows】
https://www.bilibili.com/video/BV1TT2mYqEXr?vd_source=78f7f9f8d415f8df9a79d831bb4f277c
重构项目文件
重构项目会重置Default_Map地图
可能造成vs没有代码提示
删除Binaries,Intermediate,Saved这3个文件
再次打开项目时会提示重构
刷新vs
解决vs没有代码提示的问题
设置动画蓝图
Loop Animation:循环动画
- 使用蓝图创建3个变量:
创建一个character蓝图类:
通过Try Get Pawn Owner获取Character,通过Character获取MoveMent Component,通过Movement Component设置Ground Speed
- 使用C++创建3个变量:
创建一个character C++类:
创建开始动画函数和循环动画蓝图:
virtual void NativeInitializeAnimation() override;
virtual void NativeUpdateAnimation(float DeltaTime) override;
void UMyAnimInstance::NativeInitializeAnimation()
{
}
void UMyAnimInstance::NativeUpdateAnimation(float DeltaTime)
{
}
创建3个变量:
获取GroundSpeed变量:
void UMyAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
MyCharacter = Cast<AMyCharacter>(TryGetPawnOwner());
if(MyCharacter)
{
MyCharacterMovement = MyCharacter->GetCharacterMovement();
}
}
void UMyAnimInstance::NativeUpdateAnimation(float DeltaTime)
{
Super::NativeUpdateAnimation(DeltaTime);
if(MyCharacterMovement)
{
GroundSpeed = UKismetMathLibrary::VSizeXY(MyCharacterMovement->Velocity);
}
}
更改动画蓝图的父类为C++类:
展示父类中创建的3个变量:
设置State Machine:
在State Machine中设置Idle和Run动作切换:
在Idle中添加Idle动画:
在Run中添加Idle动画:
设置Idle切换到Run的蓝图:
设置Run切换到Idle的蓝图:
动作映射
绑定跳跃动作:
PlayerInputComponent->BindAction(FName("Jump"), IE_Pressed, this, &ACharacter::Jump);//Jump函数调用的只是ACharacter中实际存在的函数
设置跳跃动画
创建IsFalling变量用于判断角色当前状态:
UPROPERTY(BlueprintReadOnly)
bool IsFalling;
if(Movement)
{
Speed = UKismetMathLibrary::VSizeXY(Movement->Velocity);
IsFalling = Movement->IsFalling();
}
将Ground Locomotion暂存:
设置Main States:
设置OnGround:
设置Inair:
设置Loop:
设置Land:
设置OnGround到InAir:
设置InAir到Loop:
设置Loop到Land:
设置Land到OnGround:
第一个箭头:
第二个箭头:
设置shift奔跑
在蓝图中设置:
在C++中设置:
在动画蓝图C++中添加WalkSpeed变量,并在Character类中设置获取WalkSpeed函数,将其值赋值给Animation类中的WalkSpeed:
设置动画蓝图:
设置脚部IK
分析:
设置collision
为静态网路添加碰撞:
设置碰撞预设:
设置忽略camera:相机会被石头阻挡
设置忽略Pawn:角色可以穿过石头
启用物理引擎:
设置Overlap Events
在蓝图中设置:
设置物体可以与Pawn重叠:
添加碰撞组件:
设置碰撞体重叠:
在游戏中显示碰撞体:
获取特定对象的重叠事件:
在C++中设置:
创建Sphere类:
UPROPERTY(EditAnywhere)
USphereComponent* Sphere;
AItem::AItem()
{
Sphere = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
Sphere->SetupAttachment(RootComponent);
}
创建重叠事件函数:
寻找委托函数:
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams//一共有6个参数
(
FComponentBeginOverlapSignature, //委托类型
UPrimitiveComponent, //委托对象
OnComponentBeginOverlap, //委托名称
UPrimitiveComponent*, OverlappedComponent,
AActor*, OtherActor,
UPrimitiveComponent*, OtherComp,
int32, OtherBodyIndex,
bool, bFromSweep,
const FHitResult &, SweepResult
);
将参数复制并删除逗号:
UFUNCTION()
void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
void AItem::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
FString OtherActorName = OtherActor->GetName();
if(GEngine)
{
GEngine->AddOnScreenDebugMessage(1, 30.f, FColor::Red, OtherActorName);
}
}
void AItem::BeginPlay()
{
Super::BeginPlay();
Sphere->OnComponentBeginOverlap.AddDynamic(this, &AItem::OnSphereOverlap);
}
创建重叠结束函数:
UFUNCTION()
void OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
void AItem::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
FString OtherActorName = OtherActor->GetName();
if(GEngine)
{
GEngine->AddOnScreenDebugMessage(1, 30.f, FColor::Blue, OtherActorName);
}
}
void AItem::BeginPlay()
{
Super::BeginPlay();
Sphere->OnComponentBeginOverlap.AddDynamic(this, &AItem::OnSphereOverlap);
Sphere->OnComponentEndOverlap.AddDynamic(this, &AItem::OnSphereEndOverlap);
}
进入范围:
离开范围:
将Item的Static Mesh设置为剑:
设置武器类
创建以Item为父类的子类Weapon:
创建Weapon蓝图类:
将父类Item中的Transfomed Sin函数继承并在子类Weapon中使用:
将父类Item中Overlap函数设置为虚函数:
在子类Weapon的protected中重写函数:
使用Super来继承父类函数中的代码:
免费动画资源
添加更多角色动画
在角色的hand骨骼中添加Socket:
添加资产预设:
添加动作预设:
添加角色:
免费动画资源
下载XBot资产:
在Slash中添加Assets文件夹:
添加Miaxamo文件夹:
将XBox资产添加到文件夹:
在虚幻中添加Miaxmo文件夹:
添加XBot文件夹:
使用Import导入资产:
整理资产名称:
添加动画资产:
设置IK Rig:
设置Retarget Root:
设置XBot的Restarget Chain:
设置Character的IK Rig:
将pelvis设置为Retarget Root:
Leg有问题,需要修改:
删除LeftLeg_0并将LeftLeg中改为ball_l:
设置IK Retargeter:
将剑附加到右手上:
在蓝图中设置:
在C++中设置:
设置按键F拿起武器:
设置拿起武器动画切换:
创建UENUM类:
站立动画:
行走动画:
跑步动画:
整理动画蓝图:
添加动画蓝图:
创建缺少的常量:
添加动画蓝图:
蒙太奇动画
设置武器攻击:
在蓝图中设置:
在C++中设置:
添加蒙太奇动画:
设置攻击键:
使用固定攻击动画2:
随机攻击动画:
将蒙太奇动画封装为函数:
设置两段动画结束标志:
添加EActionState枚举类,用于判断当前是否在执行动作
设置Attack函数:
设置AttackEnd函数:
在蓝图中设置当动画结束时调用AttackEnd函数:
设置蒙太奇动画:
解决武器拿起问题
解决武器拿起跳动问题:
删除蓝图中的节点:
在C++中设置:
创建是否手持武器的枚举类:
如果没有手持武器,则武器上下跳动:
在Weapon中的Equip函数中设置ItemState:
解决在攻击动画期间可以跳跃和移动,以及在跳跃期间可以攻击的问题:
增加跳跃的虚函数:
在移动和跳跃前判断当前状态:
更改跳跃函数:
在跳跃期间不可攻击:
设置声音
添加声音:
使用UE4旧内容设置:
使用UE5新内容设置:
设置MetaSound:
设置随机音量和声调:
- 添加角色喘气音效:
设置随机音效:将多种声音作为数组传递
- 添加角色跑步音效:
开启Enable Shared State:将声音用于共享的动作中从而共享声音
在固定跑步动画中设置,而不是蒙太奇动画中设置:
- 添加重物晃动的声音:
- 添加跑步时衣服声音:
- 添加跳跃声音:
添加落地声音:
添加起跳声音:
- 添加拿取Weapon时的声音:
添加PouchHeavy声音:
添加Shink声音:
只在装备的一瞬间发出声音:
为Equip Sound添加资产:
- 添加Niagara跑步时的特效:
设置特效产生的位置:
装备武器和卸下武器
添加蒙太奇动画:
添加Arm和Disarm事件:
重构函数:
- 设置奔跑跳跃时不能切换武器:
添加EquippingWeapon状态
修改ActionState:
跳跃时禁止切换:
添加动作结束标志:
添加结束函数,将ActionState重新设置为Unoccupied:
将结束标志链接到结束函数上:
修复武器挥动时可以按F发出声音的问题
拿起武器时,Overlapping Item是None
挥动武器时,由于武器碰撞体与Character相撞,导致Overlapping Item变为BP_Weapon
将碰撞球体放到protected类中:
若装备了武器,则将碰撞球体设置为无碰撞:
编辑动画
013 Editing Animations
1.先确定好开始和最后的动画位置,并将需要修改的每个骨骼点击Key键
2.在动画中间部分调整需要调整的骨骼,直接按Key键(不需要每个都按一下)
作为新动画导出:
设置Weapon碰撞体
设置测试碰撞球体:
勾选墙体的Generate Overlap Events:
设置Tracing
在蓝图中设置:
添加两个Scene:
添加Trace:
设置武器攻击角色时也能显示效果:
将WeaponBox的Pawn设置为Ignore:
添加一个角色实例并设置实例:
在C++中设置:
设置只在攻击动画时使用Tracing
在动画蓝图中添加EnableCollison和DisableCollison并在蓝图面板中使用:
先将开始时的WeaponBox中的CollisonEnable设置为NoCollision:
获取私有变量WeaponBox:
在SlashCharacter中添加SetWeaponCollision函数并暴露到蓝图面板中使用:
在蓝图面板中设置EnableCollision:
添加Enemy
添加Enemy的C++类:
添加Enemy蓝图类:
设置Blender
设置Blender以转换没有root的角色
009 Root Motion Animations
Mixamo 转换器
添加接口
设置被攻击montage动画
创建并设置蒙太奇动画:
创建动画蓝图:
启用EnableRootMotion,从而使Enemy被攻击时按root移动:
设置被攻击移动方向:
- 设置一次攻击只会移动一次:
在public中创建一个IgnoreActors数组,每次重叠时将重叠物体放入IgnoreActors数组,然后重叠结束时清除IgnoreActors数组:
在DiableCollision中清除IgnoreActors:
添加被攻击音效
重构函数:
在ImpackPoint位置Play Sound:
设置音效衰减
测试函数:
设置血液特效
设置剑残影特效
添加剑头和剑尾的插件:
在montage动画中添加Trail:
按V切换第一人称
按E查看Node
设置按键可视化交互
添加可破碎的罐子
将0破碎,使每个碎片破碎为更小的碎片:
设置场地系统用于破坏罐子
先添加外部压力破碎罐子,然后添加线性压力使碎片飞走:
设置武器攻击破坏罐子
在C++中设置蓝图函数:
设置蓝图函数:
设置Destructible的Collision Type:
创建罐子类
创建C++类:
设置Module:
创建蓝图类:
设置Blueprint Native Event
在BreakableActor中重写GetHit:
设置击碎罐子声音
设置罐子破碎后消失
免费声音资源
添加宝藏
添加拾取声音:
添加宝藏类:
创建Item的子类:
重写虚函数:
创建BP蓝图类:
设置宝藏的Mesh和Sound:
设置破坏罐子spawn宝藏
在蓝图中设置:
在C++中设置:
设置TSubclassOf:指定蓝图中特定的类
设置出现随机宝藏
添加更多可破坏的罐子
先添加Geometry Collection:
将Collision设置为Box:
创建子类BP类并重新设置Mesh和Capsule大小:
设置宝藏种类:
设置无宝藏罐子
复制BP罐子而不是创建子类:
将宝藏种类设置为空:
创建Niagara
设置Niagara:
在C++中创建Niagara组件:
先在Slash.Build.cs中添加Niagara并在关闭虚幻然后在vs中启动虚幻:
在Treasure中设置Niagara组件:
在Weapon中设置Niagara组件:
在C++中设置equip时停用Niagara组件:
添加Health组件
创建Health组件:
将组件附加到Enemy上:
结果:
创建Health的Widget
创建添加Widget的C++类:
创建HealthBarComponent的C++父类HealthBar
将父类设置为WealthBar:
在HealthBarComponent中添加设置健康百分比函数:
将Enemy中原先的UWidgetComponent改为UHealthBarComponent:
设置Damage使Health减少
在AActor类中查找TakeDamage函数:
将TakeDamage函数添加到Enemy中:
在Equip函数中添加参数并重新设置所有Equip函数:
添加ApplyDamage函数:
添加ReceiveDamage和GetHealthPercent函数:
设置TakeDamage函数:
设置自定义Widget样式