1. 网格组件 UStaticMeshComponent
指定什么样的网格,就显示什么样的形状。
头文件:#include "Components/StaticMeshComponent.h"
声明:UStaticMeshComponent *CollectableMesh;
2. 碰撞体组件
检测两个物体的碰撞
3.
EditDefaultsOnly:该属性可通过属性窗口来编辑,但仅能对原型编辑
EditAnywhere:该属性可从编辑器内的属性窗口编辑。
1. 网格组件 UStaticMeshComponent
指定什么样的网格,就显示什么样的形状。
头文件:#include "Components/StaticMeshComponent.h"
声明:UStaticMeshComponent *CollectableMesh;
2. 碰撞体组件
检测两个物体的碰撞
3.
EditDefaultsOnly:该属性可通过属性窗口来编辑,但仅能对原型编辑
EditAnywhere:该属性可从编辑器内的属性窗口编辑。
PacManGameModeBase.h
#include "Public/Enemy.h"
UCLASS()
class PACMAN_API APacManGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
// 易受攻击的方法
void SetEnemyVulnerable();
private:
// 定义一个数组,保存所有的敌人
TArray<class AEnemy*> Enemys;
};
PacManGameMode.cpp
#include "Public/EngineUtils.h"
// 易受攻击的事情,用 GameMode 来处理所有的敌人,在 Character 中直接调用 GameMode 的这个方法,就可以把所有的敌人都设置成易受到攻击的状态,而不必分别设置了
// 设置成易受攻击,被主角吃的行为就好了
void APacManGameModeBase::SetEnemyVulnerable()
{
// 遍历所有的Enemy,调用它身上的SetVulnerable方法就可以了
// Iter 表示存在的话,Iter++
for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
{
// 调用它身上的SetVulnerable方法
(*Iter)->SetVulnerable();
}
}
void APacManGameModeBase::BeginPlay()
{
SetCurrentState(EGameState::EPlaying);
// 使用迭代器,需要引入头文件:Public/EngineUtils.h
// 获得所有的敌人 ;起个名字:enemyItr; GetWorld():从世界得到; enemyItr 存在的话就去遍历
for (TActorIterator<AEnemy> enemyItr(GetWorld()); enemyItr; ++enemyItr)
{
// 强制转化成 AEnemy
AEnemy* enemy = Cast<AEnemy>(*enemyItr);
// 转化后,看存不存在
if (enemy)
{
// 如果存在的话,就添加到数组里面
Enemys.Add(enemy);
}
}
}
PacManCharacter.cpp
void APacManCharacter::OnCollision(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
// 当游戏正在运行才去检测碰撞
if (GameMode->GetCurrentState() == EGameState::EPlaying)
{
// 调用易受攻击的状态,什么时候调用:就是在主角吃到了超级大力丸的时候调用,把敌人设置成易受攻击的状态,找到主角,找到碰撞函数 OnCollision
// 碰到是食物的话,就把它销毁
// 当碰撞的对象是 ACollectables 类时销毁掉
// 使用自己定义的类AColletables,也需要引入对应的头文件: #include "Public/Collectables.h"
if (OtherActor->IsA(ACollectables::StaticClass()))
{
// 吃到豆子
// 强制转化成豆子
ACollectables* collectable = Cast<ACollectables>(OtherActor);
// 判断是不是大力丸
if (collectable->bIsSuperCollectable)
{
// 把敌人变成易受攻击的状态
GameMode->SetEnemyVulnerable();
}
// 销毁豆子
OtherActor->Destroy();
// 检测一下豆子的数量是否为 0
if (--CollectablesToEat == 0)
{
GameMode->SetCurrentState(EGameState::EWin);
}
UE_LOG(LogTemp, Warning, TEXT("Remain Collectable is %d"), CollectablesToEat);
}
}
}
GameMode 是定制游戏规则的地方
按下 P 键,所有的敌人都不能移动
GameMode 来管理所有的敌人
把敌人易受攻击的状态设置
单独一个一个设置太麻烦了
在GameMode 里面获得所有敌人
调用易受攻击的方法就可以了
GameModeBase 获得所有敌人,需要引入头文件:敌人
控制游戏的暂停:就是控制怪物的移动
输入switch,Tab 两下,在switch_on中按住Tab输入value,然后回车,自动补全
Enemy.cpp
#include "Public/AIEnemy.h"
AEnemy::AEnemy()
{
// 获得AI,设置好AIController
AIControllerClass = AAIEnemy::StaticClass();
}
// 是否移动
void AEnemy::SetMove(bool bMoveIt)
{
// 获得AI
AAIEnemy* AI = Cast<AAIEnemy>(AIControllerClass);
// 移动
if (bMoveIt)
{
// 找下一个点
AI->SearchNewPoint();
}
else
{
// 停止移动
AI->StopMove();
}
}
PacManGameModeBase.cpp
// 设置当前状态
void APacManGameModeBase::SetCurrentState(EGameState value)
{
currentState = value;
// 判断暂停等状态
// 输入switch,Tab 两下,在switch_on中按住Tab输入value,然后回车,自动补全
switch (value)
{
case EGameState::EPlaying:
// 遍历所有的敌人
for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
{
// 可以移动
(*Iter)->SetMove(true);
}
break;
case EGameState::EPause:
// 遍历所有的敌人
for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
{
// 不可以移动
(*Iter)->SetMove(false);
}
break;
case EGameState::EWin:
// 遍历所有的敌人
for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
{
// 销毁敌人
(*Iter)->Destroy();
}
break;
case EGameState::EGameOver:
// 遍历所有的敌人
for (auto Iter(Enemys.CreateIterator()); Iter; Iter++)
{
// 销毁敌人
(*Iter)->Destroy();
}
break;
default:
break;
}
}
void APacManGameModeBase::BeginPlay()
{
// 设置成菜单模式,开局有UI提示
SetCurrentState(EGameState::EMenu);
}
网格组件定义UStaticMeshComponent
球体碰撞体组件定义USphereComponent
球体碰撞体组件头文件Components/SphereComponent.h
UPROPERTY(EditDefaultsOnly, Category = Collectable)//属性
UPROPERTY(EditAnyWhere, Category = Collectable)//属性
PacManCharacter.cpp
void APacManCharacter::MoveXAxis(float AxisValue)
{
// 只有游戏开始的时候才能移动
if (GameMode->GetCurrentState() == EGameState::EPlaying)
{
CurrentVelocity.X = AxisValue;
AddMovementInput(CurrentVelocity);
}
}
void APacManCharacter::MoveYAxis(float AxisValue)
{
// 只有游戏开始的时候才能移动
if (GameMode->GetCurrentState() == EGameState::EPlaying)
{
CurrentVelocity.Y = AxisValue;
AddMovementInput(CurrentVelocity);
}
}
Enemy.cpp
AEnemy::AEnemy()
{
// 启用碰撞函数
SetActorEnableCollision(true);
}
void AEnemy::BeginPlay()
{
Super::BeginPlay();
// 注册碰撞函数
GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &AEnemy::OnCollision);
}
// 是否移动
void AEnemy::SetMove(bool bMoveIt)
{
// 获得AI
// 最后括号里面的AIControllerClass弃用 换成 GetController()
AAIEnemy* AI = Cast<AAIEnemy>(GetController());
// 移动
if (bMoveIt)
{
// 找下一个点
AI->SearchNewPoint();
}
else
{
// 停止移动
AI->StopMove();
}
}
项目设置---引擎---Collision---新建对象通道
新建通道:
Preset:
新建
新建概述文件:
只需要和墙、地板发生碰撞,静态
Block :区块
区块:就是阻碍的意思
EnemyBody---碰撞预设值 改成 Enemy
CapsuleComponent---碰撞预设值 也改成 Enemy
解决场景中的敌人不移动的办法:
把场景中的敌人都删除,然后再重新拖入试一下
这样场景中的敌人就可以移动了
注册碰撞函数
启用碰撞函数:
主角的碰撞预设值是:Pawn
重叠:Overlap,一致才能发生作用
Enemy.cpp
void AEnemy::BeginPlay()
{
Super::BeginPlay();
// 刚开始的速度太大了,设定初始的速度小一些
GetCharacterMovement()->MaxWalkSpeed = 150.0f;
}
void AEnemy::Killed()
{
if (bIsDead)
{
return;
}
bIsDead = true;
GetCharacterMovement()->MaxWalkSpeed = 300.0f;
// 获得AI
AAIEnemy* AI = Cast<AAIEnemy>(GetController());
// 让敌人回家
AI->GoHome();
}
void AEnemy::ReArm()
{
bIsDead = false;
GetCharacterMovement()->MaxWalkSpeed = 150.0f;
if (bIsVulnerable)
{
SetInVulnerable();
}
// 重新出来
SetMove(true);
}
AIEnemy.h
#include "PacManGameModeBase.h"
UCLASS()
class PACMAN_API AAIEnemy : public AAIController
{
GENERATED_BODY()
private:
APacManGameModeBase* GameMode;
};
AIEnemy.cpp
#include "Kismet/GameplayStatics.h"
void AAIEnemy::OnPossess(class APawn* InPawn)
{
Super::OnPossess(InPawn);
// 获取GameMode // UGameplayStatics 需要引入 #include "Kismet/GameplayStatics.h"
GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
}
void AAIEnemy::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult & Result)
{
// 当敌人不是死亡的时候 并且 游戏不是暂停的时候
if (!Bot->bIsDead &&GameMode->GetCurrentState() != EGameState::EPause)
{
// 找新的随机点
SearchNewPoint();
}
}
void AAIEnemy::StopMove()
{
//StopMovement();
MoveToLocation(Bot->GetActorLocation());
}
安装Window 8.1SDK
https://developer.microsoft.com/zh-cn/windows/downloads/sdk-archive
Content 对应 内容浏览器窗口下的 内容
内容下有 StarterContent
Soucre 对应 内容浏览器下的 C++类
MyPacManGameModeBase
设置默认打开的关卡和游戏运行的关卡:
编辑--项目设置---地图和模式---Default Maps
显示UI,需要继承HUD这个类:
创建基于PacManHUD的蓝图类
我们自定义的HUDFont需要指定字体:视图选项中 勾选 显示引擎内容,方可选择 RobotoDistanceField 字体。
修改完蓝图需要编辑保存
对 BP_PacManGameMode 的 HUD Class 指定成我们刚刚转化的 BP_PacManHUD 。
Canvas->SizeX / 2.0f , Canvas->SizeY / 2.0f 显示的文字不在正中间
PacManHUD.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "PacManHUD.generated.h"
/**
*
*/
UCLASS()
class PACMAN_API APacManHUD : public AHUD
{
GENERATED_BODY()
public:
// UFont 字体,显示的文字,字体,为了寻找字体方便,修改字体方便,一个蓝图类来继承它,在蓝图中修改字体
// EditAnyWhere 在哪块都能修改
// BlueprintReadWrite 表示可以在蓝图中修改这个属性/定义
// Category 分类,分到HUDFont类中去
UPROPERTY(EditAnyWhere,BlueprintReadWrite,Category=HUDFont)
UFont* HUDFont;
// 介绍一个函数,这个函数很重要,在屏幕上显示文字,靠这个函数来实现的
// DrawHUD 就像 Tick 一样,每帧调用一次,这样把我输入的文字显示在屏幕上
virtual void DrawHUD() override;
};
PacManHUD.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Public/PacManHUD.h"
#include "PacManGameModeBase.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/Canvas.h"
#include "Public/PacManCharacter.h"
void APacManHUD::DrawHUD()
{
// 获得当前的游戏模式
class APacManGameModeBase* GameMode = Cast<APacManGameModeBase>(UGameplayStatics::GetGameMode(this));
// 根据不同的状态,显示不同的文字
switch (GameMode->GetCurrentState())
{
case EGameState::EMenu:
// 显示文字的函数,
// 第一个参数:需要显示的文字;
// 第二个参数:文字的颜色;
// 第三个参数:获得横的屏幕的宽度;
// 第四个参数:获得纵的屏幕的长度
// 第五个参数:显示的字体
// (Canvas->SizeX/2.0f) 就是在屏幕中间
// 使用 Canvas 需要引入头文件:#include "Engine/Canvas.h"
// 向下,向右为正方向,为了移动正中心,进行了减法运算到屏幕中间
DrawText(TEXT("Welcome To PacMan!\n\nN to start a new game \nP to pause the game"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
break;
case EGameState::EPlaying:
{// 这个括号需要
// 获得主角,0 表示第一个玩家,场景只有一个玩家
APacManCharacter* PacMan = Cast<APacManCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));
if (PacMan)
{
// FString::FromInt() 将 int类型的变量转换成字符串
FString LiveString = TEXT("Lives: ") + FString::FromInt(PacMan->Lives);
DrawText(LiveString, FColor::Green, 50, 50, HUDFont);
FString CollectablesToEatString = TEXT("CollectablesToEat: ") + FString::FromInt(PacMan->CollectablesToEat);
DrawText(CollectablesToEatString, FColor::Green, Canvas->SizeX - 150, 50, HUDFont);
}
}// 这个括号需要
break;
case EGameState::EPause:
DrawText(TEXT("P To Continue"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
break;
case EGameState::EWin:
DrawText(TEXT("You Win!\n\n R for another"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
break;
case EGameState::EGameOver:
DrawText(TEXT("CameOver!\n\nR for another"), FColor::White, (Canvas->SizeX / 2.0f) - 150.0f, (Canvas->SizeY / 2.0f) - 100.0f, HUDFont);
break;
default:
break;
}
}
PacManCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PacManGameModeBase.h"
#include "PacManCharacter.generated.h"
UCLASS()
class PACMAN_API APacManCharacter : public ACharacter
{
// 改成了Public可供PacManHUD显示调用
public:
// 需要吃的 豆 的数量
int CollectablesToEat;
// 当前的生命值
int Lives;
};
编辑器画刷:
模式---Geometry---Box
模式---几何体编辑器
拖动箭头可以改变几何体的大小
Box画刷:细节面板下的Brush Settings 改变XYZ
改变Y,变成1000
变长了:
Additive 是增加:
Subtractive 是减少
挖洞:挖坑:
需要在几何体编辑器下,操作,选择点,选好后,不用再按下Ctrl键了,拖动成想要的图形:
不仅仅可以选择边,还可以选择点,按住Ctrl选择2个点,往下拉,拉成了一个有坡度的几何体:
还可以单独选择某一个点,拉
迷宫的图:
选中一个物体
Ctrl + W 复制一个物体,
地面是1000*1000
文件夹归类:
材质:
StarterContent/Materials
世界大纲视图:
右键选择【画刷】,弹出菜单,点【选择】---【选择所有表面】
这样就选择了所有画刷的面:
选择需要的材质拖放到 细节--表面材质--元素0
修改后的效果:
更改地面的材质:
Ctrl + S 保存关卡
新建自定义的材质:
材质名称以 M开头
如:M_PacMan_Yellow
双击材质,进入编辑材质
按住 V 键,按下鼠标左键,弹出:
双击这里,弹出下面的 【颜色选择器】
按住 E 键,按下鼠标左键,出来这么个:
单一数字给,金属性的:
另一种打开颜色编辑器的方式:
将本工程的模型导出,使得其他工程也可用:
拖进FBX文件进入工程里面:
新建C++代码:为豆创建的代码
命名为:Collectables,并设置为 公有
报红因为,Collectables.h和Collectables.cpp文件夹目录不一致
解决报红:在引入的头文件中加上Public/
#include "#Public/Collectables.h"
#include "Collectables.generated.h" 这个一定要放在所有引入的头文件的最下面,不然Unreal编译不能通过
编码规范:
http://api.unrealengine.com/CHN/Programming/Development/CodingStandard/index.html
命名规则
命名(如类型或变量)中的每个单词需要大写首字母,单词间通常无下划线。例如:Health和UPrimitiveComponent,而非lastMouseCoordinates或delta_coordinates。
类型名前缀需使用额外的大写字母,用于区分其和变量命名。例如:FSkin为类型名,而Skin则是FSkin的实力。
模板类的前缀为T。
继承自 UObject 的类前缀为U。
继承自 AActor 的类前缀为U。
继承自 SWidget 的类前缀为S。
抽象界面类的前缀为I。
列举的前缀为E。
布尔变量必须以 b 为前缀(例如 bPendingDestruction 或 bHasFadedIn)。
其他多数类均已F为前缀,而部分子系统则以其他字母为前缀。
Typdefs 应以任何与其类型相符的字母为前缀:若为结构体的Typedefs,则使用F;若为 UObject 的Typedefs,则使用U,以此类推。
特别模板实例化的Typedef不再是模板,并应加上相应前缀,例如:
typedef TArray<FMytype> FArrayOfMyTypes;
C#中省略前缀。
多数情况下,UnrealHeaderTool需要正确的前缀,因此添加前缀至关重要。
类型和变量的命名为名词。
方法名是动词,以描述方法的效果或未被方法影响的返回值。
变量、方法和类的命名应清楚、明了且进行描述。命名的范围越大,一个良好的描述性命名就越重要。避免过度缩写。
所有变量应逐个声明,以便对变量的含义提供注释。其同样被JavaDocs格式需要。变量前可使用多行或单行注释,空白行为分组变量可选使用。
所有返回布尔的函数应发起true/false的询问,如 IsVisible() 或 ShouldClearBuffer() 。
程序(无返回值的函数)应在Object后使用强变化动词。一个例外是若方法的Object是其所在的Object;此时需要以上下文来理解Object。避免以“Handle”和“Process”为开头;此类动词会引起歧义。
若函数参数通过引用传递,同时该值会写入函数,建议以“Out”作为函数参数命名的前缀(非必需)。此操作将明确表明传入该参数的值将被函数替换。
若In或Out参数同样为布尔,以b作为In/Out的前缀,如bOutResult。
返回值的函数应描述返回的值,命名应说明函数将返回的值。此规则对布尔函数极为重要。请参考以下两个范例方法:
// True的意义是什么?
bool CheckTea(FTea Tea)
// 命名明确说明茶是新鲜的
bool IsTeaFresh(FTea Tea)
范例
float TeaWeight;
int32 TeaCount;
bool bDoesTeaStink;
FName TeaNam;
FString TeaFriendlyName;
UClass* TeaClass;
USoundCue* TeaSound;
UTexture* TeaTexture;
先在虚幻里面点击编译,再点击DebugGame Editor,再点击Development,解决报红
显示所有类:
使用 USphereComponent* 需要引入的头文件:
#include "Components/SphereComponent.h"
// 控制显示
UStaticMeshComponent* CollectableMesh;
// 控制碰撞
USphereComponent* BaseCollisionComponent;
给类添加标识是 UCLASS()
给属性添加标识是UPROPERTY()
//Collectables.h
// EditDefaultsOnly 可以在编辑器里面编辑,EditDefaultsOnly 只能对其原型进行修改;Category 分类
UPROPERTY(EditDefaultsOnly, Category = Collectable)
UStaticMeshComponent* CollectableMesh;
UPROPERTY(EditDefaultsOnly, Category = Collectable)
USphereComponent* BaseCollisionComponent;
// EditAnyWhere 不仅可以对其原型进行修改,还可以对其实例修改
UPROPERTY(EditAnywhere, Category = Collectable)
bool bIsSuperCollectable;
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Collectables.generated.h"
UCLASS()
class PACMAN_API ACollectables : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ACollectables();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// EditDefaultsOnly 可以在编辑器里面编辑,EditDefaultsOnly 只能对其原型进行修改;Category 分类
UPROPERTY(EditDefaultsOnly, Category = Collectable)
UStaticMeshComponent* CollectableMesh;
UPROPERTY(EditDefaultsOnly, Category = Collectable)
USphereComponent* BaseCollisionComponent;
// EditAnyWhere 不仅可以对其原型进行修改,还可以对其实例修改
UPROPERTY(EditAnywhere, Category = Collectable)
bool bIsSuperCollectable;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Public/Collectables.h"
#include "Public/UObject/ConstructorHelpers.h"
// Sets default values
ACollectables::ACollectables()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
// 等于true,会使得下面这个函数(Tick)每帧运行,这个是豆的,不需要这个,设置成false
PrimaryActorTick.bCanEverTick = false;
// 开启碰撞功能(默认不开启碰撞功能)
SetActorEnableCollision(true);
// 创建网格组件
CollectableMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CollectableMesh"));
// 创建碰撞体组件
BaseCollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("BaseCollisionComponent"));
// 将 碰撞体 和 网格 绑定到一起
CollectableMesh->AttachTo(BaseCollisionComponent);
// 使用 ConstructorHelpers 需要引入的头文件:#include "Public/UObject/ConstructorHelpers.h" 找到了想要的变成的球体
static ConstructorHelpers::FObjectFinder<UStaticMesh> Sphere(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
if (Sphere.Succeeded())
{
CollectableMesh->SetStaticMesh(Sphere.Object);
}
// 指定球的大小和尺寸
CollectableMesh->SetWorldScale3D(FVector(0.3, 0.3, 0.3));
BaseCollisionComponent->SetSphereRadius(16);
}
// Called when the game starts or when spawned
void ACollectables::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ACollectables::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// 等于true,会使得下面这个函数(Tick)每帧运行,这个是豆的,不需要这个,设置成false
PrimaryActorTick.bCanEverTick = true;
使用UStaticMeshComponent 需要引入的头文件:
#include "Components/StaticMeshComponent.h"
使用 ConstructorHelpers 需要引入的头文件:
#include "Public/UObject/ConstructorHelpers.h"
不改成VisibleAnywhere,细节 面板上没有修改其材质的功能。
点击 小眼睛 ,不是消失了,只是看不到样子罢了,实际上还存在的
选择4个Collectable,勾选其细节面板上的 Is Super Collectable
Collectable 代码里面的定义的 IsSuperCollectable ,EditAnyWhere
然后改成 红色,区别普通食物
删除Actor的2种方式:
选中物体,使用快捷键 Delete 键 可以删除
或
选择物体右键,在【编辑】里面找到Delete删除
使用蓝图来创建Actor,豆:
1,选中 【内容】,【添加新项】,【新建资源】,【蓝图类】
2,选择父类:选择为Actor:修改名字:改名后:
3,双击打开:
4,选中BP_Co,添加碰撞功能:添加 Sphere Collision:添加后,默认名字为:Sphere,把 Sphere 拖动到 DefaultSceneRoot 这里,替换根组件,替换后显示成这样:,代码中 Radius 设置成16,这里也改成16:
5,在 Sphere 下添加 组件:添加Static Mesh 组件:指定 Static Mesh :,选择 Shape_Sphere :,修改Static Mesh 的大小/缩放,改成 0.3,0.3,0.3
6,拖动 Static Mesh 与 Sphere Collision 重合,形状包裹一直:
强制删除即可:,没有什么用,C++代码已经实现了这个功能,蓝图也可以同样实现这个功能:
蓝图和C++代码相结合
// EditDefaultsOnly 可以在编辑器里面编辑,EditDefaultsOnly 只能对其原型进行修改;Category 分类
// VisibleAnywhere 在任何地方都可以看到它,创建实例后,仍然可以修改它;不改成VisibleAneywhere,无法修改其材质
UPROPERTY(VisibleAnywhere, Category = Collectable)
UStaticMeshComponent* CollectableMesh;
新建C++类,选择父类为Character,改名为PacManCharacter 公有,
解决报红:
#include "PacManCharacter.h"
改成:
#include "Public/PacManCharacter.h"
创建Actor类和Character类的区别
Character类.h文件里面有:
Virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) overridee;
输入与某个键绑定的
如何绑定:
回到Unreal编辑器里面,选择【编辑】---【项目设置】,在【引擎】下面有【输入】
在这里绑定
Bindings
Action Mappings(行为映射)
Axis Mappings(轴映射)
动作和按键映射提供了一种通过在输入行为和激活该行为的按键之间插入一个中间层来方便地映射按键和坐标轴的机制。
动作映射针对按键按下和释放,而坐标轴则针对具有连续范围的输入。
比如:
跳起来,按一下空格键,动作映射;
移动,按住前进键,一直向某个方向移动,需要连续范围的输入,轴映射。
动作映射 返回 true 和 false;
轴映射 返回 -1 到 1 之间的值
添加Axis Mappings +
左为负,右为正;
人为规定,所以修改向左键为-1
添加向右键:
添加Y轴方向的移动:
在PacManCharacter.h 文件中,添加
void MoveXAxis(float AxisValue);
void MoveYAxis(float AxisValue);
// 定义一个向量
FVector CurrentVelocity;
在 PacManCharacter.cpp 中
APacManCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 第一个参数,绑定的轴的名字,与编辑器里面对应的;
// 第二个参数,this,绑定这个类上;
// 第三个参数,绑定的哪个函数
// InputComponent 报红,引入#include "Components/InputComponent.h"头文件解决
InputComponent->BindAxis("MoveX", this, APacManCharacter::MoveXAxis);
InputComponent->BindAxis("MoveY", this, APacManCharacter::MoveYAxis);
}
void APacManCharacter::MoveXAxis(float AxisValue)
{
CurrentVelocity.X = AxisValue;
AddMovementInput(CurrentVelocity);
}
void APacManCharacter::MoveYAxis(float AxisValue)
{
CurrentVelocity.Y = AxisValue;
AddMovementInput(CurrentVelocity);
}
// 传递的引用,需要加上 & 符号
InputComponent->BindAxis("MoveX", this, &APacManCharacter::MoveXAxis);
InputComponent->BindAxis("MoveY", this, &APacManCharacter::MoveYAxis);
// InputComponent 改成 函数对应中的参数 PlayerInputComponent
void APacManCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveX", this, &APacManCharacter::MoveXAxis);
PlayerInputComponent->BindAxis("MoveY", this, &APacManCharacter::MoveYAxis);
}
位于C++类下脚本文件,Character脚本
创建 BluePrints 文件夹,用来保存所有的蓝图类:
右键PacManCharacter脚本,选择【创建基于PacManCharacter的蓝图类】
命名您的新 Pac Man Character
蓝图类命名前缀加上 BP_
保存在 BluePrints 文件夹里面
创建蓝图类后:
创建蓝图类后,BluePrints文件夹下有这个
小雪花代表没保存,保存后小时,不是视频中说的代表蓝图类
在蓝图编辑器中,选中 BP_PacManCharacter
添加 Static Mesh 组件,用来显示形状
选中 StaticMesh 后,在 细节 面板 找到 Static Mesh ,指定为:Shape_Sphere 球体(因为主角也是球体)
更改主角的材质:改成指定的 M_PacMan_Yellow 材质:
给主角添加上 2 个眼睛:
选中StaticMesh ,按下 Ctrl + W 复制出来一个 StaticMesh
改成 Eye1
选中后,调节缩放为0.3,0.3,0.3
拖动到合适的位置
修改颜色:
改成黑色的眼睛:
材质选择的为:M_LightStage_Skybox_Black
更改颜色后,显示:
淡蓝色的箭头 代表 正对的方向
拖动到StaticMesh下,父子物体,方便修改对应位置
修改碰撞器属性:,选择CapsuleComponent
大小改成如下:
把 Static Mesh 往下移动 正中间
碰撞器和网格重合
修改完成后,保存Save和编译
初学者
C++
物体碰撞控制 网格
正黄色:
RGB={1,1,0}
设置GameMode,定制游戏规则的一个类:
先找到C++类下,MyPacMan文件夹下的 MyPacManGameModeBase
双击 MyPacManGameModeBase 进入代码编辑
以 GameMode 为父类 ,创建一个基于它的C++蓝图类
右键MyPacManGameModeBase,【创建基于MyPacManGameModeBase的蓝图类】
命名您的新 MyPacManGameModeBase
蓝图类命名加前缀 BP_
保存位置在BluePrints文件夹下
/*
此项目创建的名称为MyPacMan
我的项目创建的名称为:PacMan
所有没有My
但是引擎好像默认就加上了My,疑问???
*/
创建后:自动打开蓝图编辑器:
Default Pawn Class 就是 默认创建的角色
修改成,我们创建的BP_PacManCharacter
修改后,先编辑,再保存
修改默认的地图模式
Default Modes
Unreal 编辑器---【编辑】---【项目设置】---【项目-- 地图&模式】
选择我们刚刚创建的 BP_MyPacManGameModeBase
Player Start 用来确定主角刚开始的位置的
删除了 Player Start ,系统默认生成主角的位置在(0,0,0)
不小心删除掉了 Player Start ,在【模式】--【基本】---【玩家起始】拖到 关卡 中
Player Start 报BADsize,是因为碰撞体 和 其他碰撞体重合了
移动到不重合的地方就没有报 BADsize
淡蓝色的箭头代表生成的主角 正对的方向:
前后键控制Y轴的移动
把Y轴当成它的正方向
却发现X轴是它的正方向
Player Start 和 BP_PacManCharacter 也是把X轴当成了 正方向
修改前:
修改后:
X轴是前后
Y轴是左右
2个碰撞体一碰就不能往前走了
C++控制逻辑
蓝图控制外部显示和交互
Content 对应编辑器内容文件夹
Source 对应界面C++类文件夹,创建出来的一些脚本也会保存到这
双击对应工程文件夹的U图标的程序,打开UE4工程
内容->新建文件夹 来保存对应关卡
编辑->项目设置->&地图&模式 Default Maps对应地图,其中Editor Startup Map为打开UE4的默认地图,下面Game Default Map为打开游戏时候的默认地图
Geometry 几何体
拉伸的两种方法:
1.模式->最右边的几何体编辑,选中某一个面来拉
2.选中右侧列表中的Box画刷->选中Brush Settings->改变X、Y、Z(注意Brush Type:Additive:对应增加,Subtractive:对应减少)
几何体编辑:选择一个点->按住ctrl->在选择一个点,将会选中一个边
盒体:X:1000,Y:10,Z:100(长,厚,高)
位置归零:变换->位置->按最右侧的拐弯箭头归零
复制物体两种方法:
(1)按住Alt一拉就出来了
(2)按住Ctrl+w,复制出新的东西,不会重合,差一点坐标轴
旋转:根据X轴,Y轴,Z轴旋转
创建场景好习惯:在右侧世界大纲视图创建文件夹来保存对应物体,来规范化,简洁化界面
如何看出地板尺寸:初始范围1000*1000,查看方法:选中Floor->找到Static Mesh->点击放大镜->左边内容中会多出引擎文件夹,不用理会,找到Floor图标,就会查看到对应体贴图尺寸
如何关闭引擎文件夹的显示:点击资源下方的视图选项->取消勾选显示引擎内容
吃豆人勾画迷宫方法:
(1)选择透视图->选择Top(俯视视角)(此为透明网格界面)
(2)选择透视图左侧三角标志->选择布局格式,常规为单个面板(此为物体显示界面)