1.error C4002 宏引用过多,应该是宏里的语法错误
2.error C4458 重命名问题
3.error C2664 ConstructorHelpers里一定要引用模板的头文件
4.error C2143: 语法错误: 缺少“;”(在“*”的前面)
error C4430: 缺少类型说明符 - 假定为 int。注意: C++ 不支持默认 int
error C2238: 意外的标记位于“;”之前
头文件互相引用的问题
在VS中项目里的.build.cs中
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
这一行的“InputCore”里后面加你想要的UE引擎中自带的模块,例如"Paper2D"
凡是加了Component的都是为资源添加在场景中渲染的能力,否则看不见
UPaperSprite是单帧的精灵,是静止的平面物体的平面资源
UPaperSpriteComponent是渲染单帧精灵的组件
为UPaperSpriteComponent类型的指针->setsprite();
UPaperFlipbook翻译成快速翻书,是多帧的精灵形成的动画资源
UPaperFlipbookComponent是渲染多帧的精灵形成的动画资源的组件
为UPaperFlipbookComponent类型的指针->setflipbook();
UBoxComponent盒体碰撞
USphereComponent圆形碰撞
UCapsuleComponent胶囊体碰撞
Outer帮助我们进行搜索范围锁定,可以填入同目录资源,如不存在填入空
getworld()注意的地方
GetWorld是UWorld世界对象指针。对于每一个在场景中存在的对象,本身都具备获取UWorld指针的能力,我们只需要调用GetWorld函数即可获得UWorld对象指针
https://blog.csdn.net/yekong1225/article/details/120236717
在GameMode.h文件中写一个此GameMode的构造参数,定义中写Default啥啥对应上就好
注意:写的构造参数为 A文件名GameModeBase()
定义中的例子:
//已构建一个Pawn类为APawn,设置其为默认Pawn类
DefaultPawnClass = APawn::StaticClass();
HUDClass=AHUD::StaticClass(); //设置AHUD为默认HUD
引入组件名.h后
U组件名* 名字=CreateDefaultSubobject<组件名>(TEXT("命个名")); //只能在构造函数中调用
一启动就崩溃检查所有类的构造函数里有没有空指针,编译完了再启动
不能在Gamemode的构造函数里写getworld()->spawnactor,因为Gamemode先生成
U组件名* 名字=NewObject(this);
【UE4】UE4中对象的创建和销毁 - 多思考多实践同等重要 - 博客园 (cnblogs.com)
在需要设置根组件的构造函数里
RootComponent=CreateDefaultSubobject
在需要设置根组件的构造函数里
name->SetupAttachment(RootComponent); //name是一个组件的对象指针的实例名称
//构建相机
//弹簧吊臂
USpringArmComponent* ArmComp = CreateDefaultSubobject
ArmComp->SetupAttachment(RootComponent);
//构建相机
UCameraComponent* MainCamera = CreateDefaultSubobject
MainCamera->SetupAttachment(ArmComp);
MainCamera->ProjectionMode = ECameraProjectionMode::Orthographic; //摄像机投射模式设置为正交模式
MainCamera->OrthoWidth = 800; //相机视口大小
//调整相机吊臂旋转
ArmComp->SetRelativeRotation(FRotator(0, -90, 0)); //旋转相机
ArmComp->bDoCollisionTest = false; //关闭摄像机吊臂的碰撞检测
//将相机的锁定权限设置到当前的Actor上,如果Actor上存在相机,则被激活
APlayerController* Pc = GetWorld()->GetFirstPlayerController(); //获取玩家0控制器
Pc->SetViewTarget(this);
//将屏幕坐标转为世界坐标
APlayerController* Pc = GetWorld()->GetFirstPlayerController();
if (!Pc)
{
return;
}
FVector Loc;
FVector Dir;
Pc->DeprojectScreenPositionToWorld(你想要获取的屏幕X轴值.f, 你想要获取的屏幕Y轴值.f, Loc, Dir); //将屏幕坐标转换为3D坐标,这样可以获取屏幕边缘的位置以进行操作,如设置空气墙,屏幕左上角屏幕X,Y值为0,0
相当于蓝图中的cast to 某某类
A某某类名* 名字 = Cast(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
Jumping();
StopJumping();
GetVelocity().Size2D() //获取2D角色的速率
重写父类中的以下虚函数
virtual void OnConstruction(const FTransform& Transform) override;
在函数中记得写
Super::OnConstruction(Transform);
这用来干嘛?
BeginPlay是在游戏运行时才开始调用,而ConstructionScript在对象在编辑器中被实例化时就会被调用一次,C++中重写上述的虚函数即可实现其调用,目的是为了显式地调整某些属性,不用每次都运行后才能调整
!!!头文件函数前面一定要记得加UFUNCTION(),否则无法调用
这个函数的定义中写碰撞后会发生什么,碰撞事件函数的格式如下
在Engine源码的PrimitiveComponent.h文件里有定义
void OnComponentHitEvent(UPrimitiveComponent* HitComponent,AActor* OtherActor,UPrimitiveComponent* OtherComp,FVector NormalImpulse,const FHitResult& Hit);
void OnComponentBeginOverlapEvent(UPrimitiveComponent* OverlappedComponent,AActor* OtherActor,UPrimitiveComponent* OtherComp,int32 OtherBodyIndex,bool bFromSweep,const FHitResult& SweepResult);
void OnComponentEndOverlapEvent(UPrimitiveComponent* OverlappedComponent,AActor* OtherActor,UPrimitiveComponent* OtherComp,int32 OtherBodyIndex);
SphereCollision->OnComponentBeginOverlap.AddDynamic(this, &ABullerActor::OnComponentBeginOverlapEvent);
//为某个组件在beginplay里绑定撞击时的回调事件,这个事件就是上面写的函数,然后开启物理通知,设置碰撞类型
某个组件->OnComponentHit.AddDynamic(this, &类AAA::OnComponentHitEvent);
某个组件->GetBodyInstance()->bNotifyRigidBodyCollision = true; //开启物理通知
某个组件->SetCollisionObjectType(ECC_某种预设类型); //设置碰撞类型
某个组件->SetCollisionProfileName(TEXT("OverlapAll")); //以碰撞文件格式设置碰撞类型
碰撞的网上的讲解例子:
https://blog.51cto.com/u_15458423/4807921
FCollisionShape Shape;
Shape.ShapeType = ECollisionShape::Sphere;
Shape.Sphere.Radius = 300;
TArray
if (OwnerComp.GetAIOwner()->GetWorld()->SweepMultiByChannel(Hits, RabbitPos, RabbitPos, FQuat(0, 0, 0, 0), ECC_Camera, Shape))
{
for (auto Hit : Hits)
{
写球形检测逻辑的地方
}
}
不要产生除了我们想要的运动关系外的关系——例如排除XY位移,旋转
FBodyInstance* Body = FlipBookComp->GetBodyInstance();
if (Body)
{
Body->bLockXRotation = true;
Body->bLockZRotation = true;
Body->bLockXTranslation = true;
Body->bLockYTranslation = true;
Body->CreateDOFLock(); //不调用创建锁定,锁定无效
}
首先要知道资源需要组件才能渲染到游戏中,静态网格体需要静态网格体组件,骨骼网格体需要骨骼网格体组件,组件一般又需要依附关系,所以从上至下应该是由对象的依附关系到各个组件到设置各个组件的资源
在构造函数中使用,如果只需要构建一次资源那么前面加static
ConstructorHelpers::FObjectFinder 起个名字(TEXT("资源引用地址"));
在其他地方中加载资源
资源类型* 资源名称=LoadObject(nullptr, TEXT("资源引用地址"));
组件类型* 组件名称.Set组件类型(资源);
//在BeginPlay函数里可以直接写
PlayerInputComponent->BindAction(TEXT("你在UE4里加的操作的绑定的名字"), IE_Pressed, this, &绑定触发哪个函数);
AddMovementInput(FVector(Value, 0, 0)); //添加角色移动的输入轴值
在PlayerController中ActivateTouchInterface函数可以设置摇杆资源
SetVirtualJoystickVisibility中可以设置隐藏或是显式摇杆
摇杆中image1为摇杆,image2为底图
虚幻中使用的是对象类序列化的特性将属性信息序列化到磁盘中
构建一个USaveGame类,然后在头文件里面写东西
public域里写成员属性,记得加UPROPERTY()
不要尝试存地址,地址存了没有用,每次打开都是不一样的
只能存基本数据类型,自定义数据类型要转成基本数据类型
APlayerController* Pc = GetWorld()->GetFirstPlayerController();
if (!Pc)
{
return;
}
if (Pc->WasInputKeyJustPressed(EKeys::Q))
{
//按Q构建存储数据类
USnakeSaveGame* SaveGame = Cast
if (SaveGame)
{
SaveGame->Num = 110;
SaveGame->Name = TEXT("OK");
//进行存储
UGameplayStatics::SaveGameToSlot(SaveGame, TEXT("Snake"), 0); //第一个存档数据对象 第二个存档名称 第三个存档位置
}
}
if (Pc->WasInputKeyJustPressed(EKeys::E))
{
USnakeSaveGame* LoadGame = Cast
if (LoadGame)
{
UE_LOG(LogTemp, Log, TEXT("%d"),LoadGame->Num);
UE_LOG(LogTemp, Log, TEXT("%s"), *LoadGame->Name);
}
}
所有的虚函数重写的定义里都加上Super::函数名();,否则可能会出错
//调用的是GENERATE_BODY里的,是调用其父类的这个函数
FTimerHandle Handle; //先创建一个定时器的句柄,句柄可以拿来重置定时器
GetWorld()->GetTimerManager().SetTimer(Handle, this, &ABullerActor::TimeOutCallBack, 0.3f);
//延迟0.3秒后调用TimeOutCallBack事件
头文件中:
UPROPERTY()
class UCurveFloat* AnimCurve;
float AnimTime;
源文件中:
Anim Time+=DeltaTime; //AnimTmie为0,为时间加上每帧调用次数
float Value = AnimCurve->GetFloatValue(AnimTime); //获取曲线在该时间的曲线值,拿来调用
检查:
float MaxTime=0;
float MinTime=0;
AnimCurve->GetTimeRange(MinTime, MaxTime); //获取曲线点的最小时间值和最大时间值
if (AnimTime>=MaxTime)
{
写你要用曲线结束来做什么事的事件
}
方法1:在DrawHUD()里, //virtual DrawHUD() override;
DrawText(TEXT("啊哈"), FLinearColor::Blue, 200, 200,Font,5,false);
方法2:用Canvas写 //屏幕宽度Canvas->ClipX; //屏幕高度Canvas->ClipY;
FText msg = NSLOCTEXT("UE4", "k1", "OK");
Canvas->SetDrawColor(FColor::Red);
Canvas->DrawText(Font, msg, 200, 100);
不失真的文字绘制:还有和之前的对比
Slate需要在.build.cs中引用模块"Slate", "SlateCore"
//绘制文字
if (Font)
{
DrawText(TEXT("啊哈"), FLinearColor::Blue, 200, 200,Font,5,false);
FText msg = NSLOCTEXT("UE4", "k1", "哦呼");
Canvas->SetDrawColor(FColor::Red);
Canvas->DrawText(Font, msg, 200, 100,5,5);
FSlateFontInfo Info(Font, 50, "Default"); //构建字体信息
FCanvasTextItem Item(FVector2D(200, 300), msg, Info, FLinearColor::Green); //构建canvas字体绘制item
Canvas->DrawItem(Item); //绘制不失真的文字
}
if (Texture)
{
//DrawTextureSimple(Texture, 0, 0);
DrawTexture(Texture, 0, 0, 300, 300, 0, 0, 1, 1);
}
void AHUD::DrawMaterial(UMaterialInterface* Material, float ScreenX, float ScreenY, float ScreenW, float ScreenH, float MaterialU, float MaterialV, float MaterialUWidth, float MaterialVHeight, float Scale, bool bScalePosition, float Rotation, FVector2D RotPivot)
void AHUD::DrawLine(float StartScreenX, float StartScreenY, float EndScreenX, float EndScreenY, FLinearColor LineColor, float LineThickness)
void AHUD::Draw3DLine(FVector Start, FVector End, FColor LineColor)
void AHUD::DrawRect(FLinearColor Color, float ScreenX, float ScreenY, float Width, float Height)
FCanvasBoxItem Item(FVector2D(0, 0), FVector2D(300, 300));
Item.SetColor(FLinearColor::Green);
Canvas->DrawItem(Item);
先在beginplay里
pPlayerController = GetWorld()->GetFirstPlayerController();
然后Tick类型调用里
if (pPlayerController->WasInputKeyJustPressed(EKeys::Q))
{
UE_LOG(LogTemp, Log, TEXT("DD"));
}
if (pPlayerController->WasInputKeyJustPressed(EKeys::RightMouseButton))
{
float MouseX = 0;
float MouseY = 0;
pPlayerController->bShowMouseCursor = true;
pPlayerController->GetMousePosition(MouseX, MouseY);
UE_LOG(LogTemp, Log, TEXT("%f,%f"),MouseX,MouseY);
}
因为DrawHUD函数是每帧调用,所以比如添加命中框为什么不会一直加?因为写了Super::DrawHUD()就会调用下面的这个函数,其中,这里的Super是指调用父类的HUD里的DrawHUD(),也就是下面这个函数,其他函数定义里要不要加Super看你自己
void AHUD::DrawHUD()
{
HitBoxMap.Reset();
HitBoxHits.Reset();
if (bShowOverlays && (PlayerOwner != nullptr))
{
FVector ViewPoint;
FRotator ViewRotation;
PlayerOwner->GetPlayerViewPoint(ViewPoint, ViewRotation);
DrawActorOverlays(ViewPoint, ViewRotation);
}
// Blueprint draw
ReceiveDrawHUD(Canvas->SizeX, Canvas->SizeY);
}
源码中的命中框事件
void ReceiveHitBoxClick(const FName BoxName);
virtual void NotifyHitBoxClick(FName BoxName);
void ReceiveHitBoxRelease(const FName BoxName);
virtual void NotifyHitBoxRelease(FName BoxName);
void ReceiveHitBoxBeginCursorOver(const FName BoxName);
virtual void NotifyHitBoxBeginCursorOver(FName BoxName);
void ReceiveHitBoxEndCursorOver(const FName BoxName);
virtual void NotifyHitBoxEndCursorOver(FName BoxName);
要使用的话必须先调用playercontroller里的bEnableClickEvents=true;
EnableInput(APlayerController* PlayerController);
InputComponent->BindAction(TEXT("UE中操作事件的名称"),IE_Pressed,this,&A此类中::InputEventName);
1.cs文件中添加以下模块
"NavigationSystem"
2.头文件引入"NavigationSystem.h"
3.相关API
UNavigationSystemV1::K2_GetRandomLocationInNavigableRadius(OwnerComp.GetAIOwner()->GetWorld(), OriginPos//黑板中的一个坐标信息, RandomLoc//声明一个随机坐标以存储变量, RandomRadius//随机的半径);
//以前版本用K2_GetRandomPointInNavigableRadius,现在要改
引擎版本4.22前可以重写Possess函数,现在都是重写OnPossess函数和OnUnPossess函数,如果行为树不激活,则看看是否是AI蓝图里的设置错误(1.AIController要用新建的AIController蓝图类不是C++类 2.以放置在场景中或已生成)
在OnPossess函数定义里
行为树组件.StartTree()
在OnUnPossess函数定义里
行为树组件.StopTree()
帧通知重写这个函数
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
状态通知重写以下三个函数
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
virtual void NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime) override;
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
重写以下函数,这些都可以在动画蓝图事件重载里找到
virtual void NativeUpdateAnimation(float DeltaTime) override;
virtual void NativeBeginPlay()override;
状态机里设置的响应动画通知的事件
UFUNCTION() //别忘了
void AnimNotify_ReloadOver();
重写以下函数
virtual void UpdateCamera(float DeltaTime) override;
小技巧
//平滑过渡使用的函数,类似带时间轴的Lerp,第一个为输入值,需要调整的变量,第二个为想要变成的,第三个,第四个类似速度
FMath::FInterpTo(CurrentCameraPosOffsetZ, 0, DeltaTime, 10);
//摄像机拉远拉近
TargetFOV
自己去翻看前面的 记不清了