BluePrint:
创建Widget蓝图,编辑控件,绑定事件,根据不同情况可能需要getPlayerController->setInputMode,在观看蓝图或者gameMode蓝图内加入该widget。
可以用Promote to Variable产生一个随时可以使用的变量,在绑定控件时就可以使用这个变量(类)中的属性和方法,如:
更改Material的时候要先把普通materail转为Dynamic Material,然后用setParameterValue来更改它的属性,例如以下根据电池电量改变人物颜色的例子:
使用timeline来设置有规律的行为,如石门平滑开启:
使用射线检测来触发雕塑事件(按下某键触发射线检测):
制作昼夜循环:
1.把lightSource改为moveable
2.打开关卡蓝图使用timeline实现光源旋转,Y轴为270~-90
也可以用Cinematics->add Matinee来创建一些动画,比如门的开关,人站到石台上石台自动升高等,先右键->new empty group,然后选中你要操作的物体,再在Mayinee内右键新建movement track或者sound track等等:如果是movemet track 则移动要操作的物体,然后在相应位置添加关键帧来完成动画:
创建顶视角Minimap步骤:
1.在characterBlueprint头顶上新建一个向下的相机(可能需要关闭autoActive以免替换主相机),相机内添加组件SceneCaptureComponent2D
2.ContentBrowser右键material->render target 新建一个,CompressionSetting设置为userInterface2D
3.把刚刚的render target放到SceneCaptureComponent2D的Texture Target中
4.右键render Target->create metarial新建一个材质,连上emmisive color会显示的更好
5.新建widget放入image控件,Image属性设置为刚刚的material
6.运行
可以创建接口蓝图,在里面定义函数,让其他蓝图实现该接口,这样可以对相同事件有不同反馈方式,比如三个box,都是鼠标点击,第一个放大,第二个缩小,第三个旋转,就可以建立鼠标点击接口,然后三个方块蓝图各自实现点击效果。以下是鼠标点击actor然后调用各actor实现的BP_pick接口的三个方法的蓝图。鼠标点击使用射线检测以及世界坐标转换:
无限跑酷中创建无限地图的方法:在一个道路块的终点后面放置一个Arrow用来当做下一块地块的放置位置,并在终点附近设置Collision Box,当人物通过Collison Box使用Spawn Actor创建一个新地块,创建的transform为上一个地块的Arrow的transform,并把这个地块arrow的trasnform记录下来为下一个地块使用。在construct Script内初始化一定数量的地块。
创建可以重复使用的component:(以悬浮组件为例子,越靠近地面收到一个更大的向上力以维持悬浮)
1.父物体设为movable
2.创建sceneComponent类命名为HoverComponent(Actor component蓝图类和Scene component蓝图类的区别在于scene component有relative position)
3.获取父物体的引用:
4.使用射线检测设置反重力的方向和大小:(add damping 是增加阻力以免越弹越高.该例子设置射线长50,离地面越近受到的力越大)
5.把该component作为任意static mesh的子component
控制相机pitch范围的方法:
1.
2.在第三人称模板中
Lerp和Clamp的区别:
lerp的值是(1-alpha)*A+alpha*B
Clamp是输入在范围内输出=输入,输入在范围外输出=边界值
使用AI跟踪玩家:创建AIcontrollerBP,在场景中添加NavMeshBoundsVolume,在AIcontroller中使用一个timer隔一段时间调用一次AIMoveTo
添加radial force component 可以使物体对其他物体产生力的影响
Projectile movement component可以快速模仿子弹运动
cast的时候右键->convert to pure cast 可以移除顺序连线接口
碰撞无效的时候记得检查Mesh是不是包含Collision组件
使用RemoveFromParent来移除Widget,OpenLevel来打开关卡,SetGamePaused来暂停游戏
使用SpawnXXXAtLocation来在特定位置产生特效或声音
Add Reroute Node 可是使蓝图看起来更整洁
实现更改游戏速度(慢放)的效果,用exctue console command :slomo 0.5 (速度变为0.5倍)
第一人称射击时镜头shake方法:新建类蓝图继承camera shake,更改参数后再第一人称蓝图内射击后调用play world camera shake即可
使用launch character可以重新设置character的速度,比如触碰到某物体时向上弹起(跳跳版)
C++:
UE4 常用标记宏:
UCLASS():
Blueprintable:可以把该类编程蓝图(类名右键)
BlueprintType:该类可以作为其他蓝图类的变量使用
NotBlueprintType :不可作为其他蓝图变量使用
UPROPERTY() :
BlueprintCallable: 属性可以在蓝图中被调用
BlueprintReadOnly:属性可以被蓝图读取,但是不可被修改
BlueprintReasWrite:属性可以被蓝图读取和修改
Category: 为属性添加分类标签
EditAnywhere:该属性可以在属性窗口中修改
EditDefaultOnly:该属性可以在属性窗口中修改,但是只能在基类的属性窗口修改
VisibleAnywhere:该属性可以在属性窗口中看见,但是不可修改
VisibleDefaultsOnly:该属性只可在基类的属性窗口中看见,而且不可修改
UFUNCTION:
BlueprintCallable:该函数可以在类蓝图或者关卡蓝图中调用
BlueprintImplementableEvent:这个函数在头文件声明但是应该在类蓝图或者关卡蓝图中重写,应该和BlueprintCallable一起使用这样蓝图就可以调用它,目的是使非程序员可针对特殊情况(这些情况不存在默认操作或标准行为)创建自定义响应。范例:在宇宙飞船游戏中玩家飞船获得强化道具时发生的事件
BlueprintNativeEvent:这个函数可以在蓝图类中重写,同时拥有C++实现,函数名为FuncName_Implementation,但是调用的时候不用加implementation,和BlueprintCallable一起使用以便在蓝图中调用.可以在蓝图中使用Add Call To Parent Function调用父方法内容
BlueprintPure: 这个函数不会改变对象的数据内容,可以用在get函数上,该函数必须有返回值
Category:为函数添加类别标签
meta说明符:
BlueprintProtected:
这个函数只能由当前对象调用,不能被其他对象调用
变量类型前缀:
模板类以T为前缀
继承自UObject的类以U为前缀
继承自AActor的类以A为前缀
继承自SWidget的类以S为前缀
抽象借口类以I为前缀
枚举类E为前缀
布尔变量以b为前缀
大部分其他类以F为前缀,如FString,FVector
UE4 支持三类字符串:
FString 类似于普通字符数组,
FName是放在全局字符串表里的,通常用来表示不可变的大小写不敏感的字符串
FText一般是表示处理本地化的字符串
和字符串有关的API以及字符串间相互转化:https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/StringHandling/index.html
如何debug:
打印到屏幕:
#include "Engine.h"
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, TEXT("Some debug message!"));
打印到output log:
UE_LOG(LogClass, Log, TEXT("You have collected %s"),*DebugString);
定时器:
FTimerHandle VarName
GetWorldTimerManager().SetTimer(this, &AMatineeActor::CheckPriorityRefresh, 1.0f, true);
GetWorldTimerManager().ClearTimer(this, &AMatineeActor::CheckPriorityRefresh);
GetWorldTimerManager().PauseTimer()
GetWorldTimerManager().UnPauseTimer()
GetWorldTimerManager().IsTimerActive()
GetWorldTimerManager().GetTimerRate()
使Pawn响应玩家输入:
AutoPossessPlayer = EAutoReceiveInput::Player0;
Clamp函数:把一个值规范化到一个范围:
CurrentVelocity.Y = FMath::Clamp(AxisValue, -1.0f, 1.0f)
引用GameplayStatics.h以使用很多有用的函数
#include "Kismet/GameplayStatics.h"
更换摄像机视角
APlayerController* OurPlayerController = UGameplayStatics::GetPlayerController(this,0)
if(camera1 = OurPlayerController->GetViewTarget() )
OurPlayerController->SetViewTarget(Camera2)
OurPlayerController->SetViewTargetWithBlend(Camera2, SmoothBlendTime)
创建带有SpringArm的相机:
OurCameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
OurCameraSpringArm->AttachTo(RootComponent); //现在用SetupAttachment
OurCameraSpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 50.0f), FRotator(-60.0f, 0.0f, 0.0f));
OurCameraSpringArm->TargetArmLength = 400.f;
OurCameraSpringArm->bEnableCameraLag = true;
OurCameraSpringArm->CameraLagSpeed = 3.0f;
OurCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("GameCamera"));
OurCamera->AttachTo(OurCameraSpringArm, USpringArmComponent::SocketName);
带有SpringArm的相机旋转视角时,yaw方向可以直接旋转Actor,Pitch方向旋转SpringArm,Putch旋转时用Clamp限定旋转角度
NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
视域缩放功能:
ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
// 基于ZoomFactor来混合相机的视域和弹簧臂的长度,Lerp把ZoomFactor映射到一个范围内的值
OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);
在代码中直接使用content browser中的素材:
static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/ParticlesP_Fire.P_Fire"));
if (ParticleAsset.Succeeded())
{
OurParticleSystem->SetTemplate(ParticleAsset.Object);
}
代码使用UMG:
在build.cs中:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
#include "Blueprint/UserWidget.h"
TSubclassOf<UUserWidget> StartingWidgetClass;
UUserWidget* CurrentWidget
CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), StartingWidgetClass);
CurrentWidget->AddToViewport();
获取一定3D空间内随机一点:
GetWhereToSpawn()返回一个boxComponent
#include "kismet/KismetMathLibrary.h"
FVector ASpawnVolume::GetRandomPointToVolume()
{
FVector MeshOrigin = GetWhereToSpawn()->Bounds.Origin;
FVector MeshExt = GetWhereToSpawn()->Bounds.BoxExtent
return UKismetMathLibrary::RandomPointInBoundingBox(MeshOrigin, MeshExt);
}
其他随机函数:
FMath::FRand()
FMath::FRandRange(MinNum,MaxNum)
枚举类型在UCLASS标记前声明:
UENUM(BlueprintType)
enum class EBatteryPlayState
{
EPlaying, //可以选择显示名称放在逗号内 UMETA(DisplayName="Dance")
EGameOver,
EWon,
EUnknown
};
EBatteryPlayState status;
OR
TEnumAsByte<EBatteryPlayState> status;
找到属于某一个类的所有Actors并存入数组:
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(),ASpawnVolume::StaticClass() , FoundActors);
判断某一个变量是不是属于某个类型只需cast一下然后看是不是空:
ASpawnVolume* TestSpawnVolume = Cast<ASpawnVolume>(myActor);
if(TestSpawnVolume!=nullptr)
{
// do something
}
找到overlap的所有Actors并存入数组,首先声明一个USphereComponent,然后:
TArray<AActor* >OverlappedActors;
CollectionSphere->GetOverlappingActors(OverlappedActors);
PlayerCharacter里可以调用很多有用方法:
UGameplayStatics::GetPlayerCharacter()
MyCharacter->getMesh()
MyCharacter->getMovementComponent()
使用TSubclassOf
TSubclassOf<Class Type> VarName 该声明方式声明该变量只能赋值为派生自Class Type的类
结构体声明:
新建一个C++类,
#include "Chapter2.h"
#include "ColoredTexture.generated.h"
USTRUCT()
struct CHAPTER2_API FColoredTexture
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category =
HUD )
UTexture* Texture;
UPROPERTY( EditAnywhere, BlueprintReadWrite, Category =
HUD )
FLinearColor Color;
};
创建可重复使用component(随机移动组件为例):
#pragma once #include "Components/ActorComponent.h" #include "RandomMovementComponent.generated.h" UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class UE4COOKBOOK_API URandomMovementComponent : public UActorComponent { GENERATED_BODY()public: URandomMovementComponent(); virtual void BeginPlay() override; virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override; UPROPERTY() float MovementRadius; }; #include "UE4Cookbook.h" #include "RandomMovementComponent.h" URandomMovementComponent::URandomMovementComponent() { bWantsBeginPlay = true; PrimaryComponentTick.bCanEverTick = true; MovementRadius = 5; } void URandomMovementComponent::BeginPlay() { Super::BeginPlay(); } void URandomMovementComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) { Super::TickComponent( DeltaTime, TickType, ThisTickFunction ); AActor* Parent = GetOwner(); if (Parent) { Parent->SetActorLocation( Parent->GetActorLocation() + FVector( FMath::FRandRange(-1, 1)* MovementRadius, FMath::FRandRange(-1, 1)* MovementRadius, FMath::FRandRange(-1, 1)* MovementRadius)); } }
使用委托(delegate与UFUNCTION绑定)举例:
在gamemod投文件UCLASS前声明:
DECLARE_DELEGATE(FStandardDelegateSignature)
FStandardDelegateSignature MyStandardDelegate; //创建委托成员变量
在另外的actor类中创建需要绑定的UFUNCTION,如“开灯”函数,在beginplay函数中获取gamemode实例,并绑定“开灯”函数:
MyGameMode->MyStandardDelegate.BindUObject(this,&ADelegateListener::EnableLight);
在需要执行委托时,使用一下代码执行委托:
AGameMode* GameMode = UGameplayStatics::GetGameMode(TheWorld);
AUE4CookbookGameMode * MyGameMode = Cast<AUE4CookbookGameMode>(GameMode);
MyGameMode->MyStandardDelegate.ExecuteIfBound();
使用以下代码解除委托:
MyGameMode->MyStandardDelegate.Unbind();
使用带参数的委托:
DECLARE_DELEGATE_OneParam(FParamDelegateSignature,FLinearColor) 其他地方相同,调用的时候传递参数即可
使用广播委托:
假设有一个TriggerVolume 来触发委托
在gamemode 头文件里声明 DECLARE_MULTICAST_DELEGATE(FMulticastDelegateSignature)
在MulticastDelegateListener 的类(灯)里面,声明变量:
FDelegateHandle MyDelegateHandle;
绑定委托:
MyDelegateHandle = MyGameMode->MyMulticastDelegate.AddUObject(this,&AMulticastDelegateListener::ToggleLight);
结束委托:
MyGameMode->MyMulticastDelegate.Remove(MyDelegateHandle);
其他类也可以通过使自己的方法绑定一个MyDelegateHandle来触发相应委托,只要调用
MyGameMode->MyMulticastDelegate.Broadcast(); 所有绑定的委托都会调用
广播委托也可以有参数
委托的缺陷是需要使用第三方类来调用委托,它的执行方法是public的
Custome Event
在MyTriggerVolume (触发事件的actor)里面声明:
DECLARE_EVENT(AMyTriggerVolume, FPlayerEntered)第一个参数是需要调用broadcast的类,第二个参数是事件函数签名的类名
在类中添加一个event signature:FPlayerEntered OnPlayerEntered;
在AMyTriggerVolume::NotifyActorBeginOverlap 中添加OnPlayerEntered.Broadcast();
创建一个TriggerVolEventListener 类(灯所在类),在里面声明:
UPROPERTY()
UPointLightComponent* PointLight;
UPROPERTY(EditAnywhere)
AMyTriggerVolume* TriggerEventSource;
UFUNCTION()
void OnTriggerEvent();
在beginplay里面绑定事件:
TriggerEventSource->OnPlayerEntered.AddUObject(this,&ATriggerVolEventListener::OnTriggerEvent)
接口Interface
要在c++中使用接口,要手动创建.h 和.cpp文件,在UE4编辑器内无法创建
//接口类 //h #pragma once #include "ReactsToTimeOfDay.generated.h" /* must have BlueprintType as a specifier to have this interface exposed to blueprints with this line you can easily add this interface to any blueprint class */ UINTERFACE(BlueprintType) class MYPROJECT_API UReactsToTimeOfDay : public UInterface { GENERATED_UINTERFACE_BODY() }; class MYPROJECT_API IReactsToTimeOfDay { GENERATED_IINTERFACE_BODY() public: //classes using this interface must implement ReactToHighNoon UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MyCategory") bool ReactToHighNoon(); //classes using this interface may implement ReactToMidnight UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "MyCategory") bool ReactToMidnight(); }; //cpp #include "MyProject.h" #include "ReactsToTimeOfDay.h" UReactsToTimeOfDay::UReactsToTimeOfDay(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } //实现接口的类: //h #include "ReactsToTimeOfDay.h" #include "ASkeletalMeshActor.generated.h" UCLASS() class AFlower : public ASkeletalMeshActor, public IReactsToTimeOfDay { GENERATED_BODY() public: /* ... other AFlower properties and functions declared ... */ UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "MyCategory") bool ReactToHighNoon(); virtual bool ReactToHighNoon_Implementation() override; }; //cpp bool AFlower::ReactToHighNoon_Implementation() { //Default behaviour for how flower would react at noon //OpenPetals(); //AcceptBugs(); //... return true; }
再次注意BlueprintNativeEvent是在cpp内有一个实现,函数名加implementation,蓝图内可重写
BlueprintImplementableEvent是必须在蓝图内实现
检查某个类是否实现了某接口,可以有2个方法:
1.把那个类cast 成 检查的interface,cast成功则实现了,null则没实现
2.使用if (SpawnedActor->GetClass()->ImplementsInterface(UMyInterface::StaticClass())) 来检查
使用cast可以把实现了相同接口的不同对象当做一类对象来使用,比如把它们放进数组