2017-12-07 13:12:12 baidu_27276201 阅读数 680
  • 从这里开始虚幻4-第2辑-蓝图 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2158 人正在学习 去看看 杨石兴

在UE4中蓝图本身已经非常强大,但是并没有强大到没有任何缺陷  --Mantra

如果你也遇到了上面的问题,可以继续往下看,我们接下来就会用C++扩展已有的蓝图功能。

首先需要创建一个特殊的C++类(需要继承自BlueprintFunctionLibrary),具体操作可以见下图


创建完成后会编译工程源码并且会自动打开VS(笔者用的是VS15),接下来我们编辑自定义功能。在做一个完整的功能之前最好先做测试,所以我们首先会写一个简单的函数来做测试(至于代码中出现的UFUNCTION之类的宏就不多做解释啦)

有几个点需要注意一下:

1)函数需要使用UFUNCTION进行修饰,否则蓝图无法调用

2)函数需要使用static修饰

3)一般定义成public,方便外部访问

案例一:测试函数

public:
	// 测试函数,主要用来测试
	UFUNCTION(BlueprintCallable, Category = "Mantra|Tool")
		static void GetCurrentActorInfo(AActor* Actor);
void UMyBlueprintFunctionLibrary::GetCurrentActorInfo(AActor* Actor)
{
	// 测试两种打印方式
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Blue, TEXT("Function Called"));
	}

	UE_LOG(LogTemp, Warning, TEXT("Called Function"));
}

在蓝图中的效果


案例2:对一个数组进行随机排序

主要代码如下:

UFUNCTION(BlueprintPure, Category = "Mantra|Tool")
		static TArray<int32> RandomizeIntArray(const TArray<int32> InArray);

	UFUNCTION(BlueprintCallable, Category = "Mantra|Tool")
		static void PrintArrayElement(const TArray<int32> InArray);

TArray<int32> UMyBlueprintFunctionLibrary::RandomizeIntArray(const TArray<int32> InArray)
{
	TArray<int32> NewArray = InArray;
	int32 temp;
	float randomNumber;

	// 利用排序算法对数组进行排序操作
	for (int32 i = NewArray.Num(); i > 1; --i)
	{
		randomNumber = FMath::RandRange(0, i - 1);
		temp = NewArray[FMath::FloorToInt(randomNumber)];
		NewArray[FMath::FloorToInt(randomNumber)] = NewArray[i - 1];
		NewArray[i - 1] = temp;
	}
	return NewArray;
}

void UMyBlueprintFunctionLibrary::PrintArrayElement(const TArray<int32> InArray)
{
	for (int32 i = 0; i < InArray.Num(); ++i)
	{
		UE_LOG(LogTemp, Warning, TEXT("Current Number is %d"), InArray[i]);
	}
}

主要功能已经完成,接下来测试

在关卡蓝图中创建Int类型的数组,之后调用我们写好的函数,可参见下图


运行效果如图


总结

我们今天只是写了一个随机排序,大家可以根据需要写出对数组的各种排序。



2016-04-23 01:13:13 xiaominghimi 阅读数 8052
  • 从这里开始虚幻4-第2辑-蓝图 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2158 人正在学习 去看看 杨石兴
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/unreal-engine-game/2164.html

首先Himi在这里解释下,为什么还是开篇… 原因主要有两点:

  1. 考虑到要写也要写点干货不是么?!但是由于官方文档写的真的够详细了,对于提到的基础知识我真的不想再赘述,因此希望各位新童鞋一定要先跟着文档动手过一遍!!!很重要!!!!!!!!!!!
  2. 另一方面,本想拿着UE4写点小项目练手,然后写点官方文档里没有的或项目中遇到的问题与重点。但是Himi最近在研究React Native,近期可能无法着手继续研究UE4,因此本篇属于开篇更合适一些。等回头正式回归继续学习研究UE4时,届时正式开始教程系列。

大家可以先进群进行交流学习,本群的管理员都是超过2年以上虚幻引擎开发老手!

   ☞ 【Unreal Engine】191452043

 

1

先引用官方文档的解释:

虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程。 新的游戏类、Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 XCode之一编译后可以在虚幻编辑器中反映出全部变更内容。 蓝图可视化脚本系统是一个强劲的工具,可以让类通过连接函数区块和属性引用来在编辑器中进行创建。

C++类可以作为蓝图类的基类使用,并且这样的话,程序员就可以设置基础的游戏类,随后,它们由关卡设计师来进行子类和迭代处理。

OK,说点通俗点:

  蓝图与C++是相互配合的,不论你通过哪种方式创建的工程,都可以在项目中创建所需的C++类或蓝图类。

针对蓝图来说:更倾向于项目前期快速构建进行试水看效果。

在Himi的理解来看,蓝图属于主导,而C++属于辅助。蓝图快速搭建架子,C++来进行重构、整理、补充!

 

简单来看下面这张图,大家更容易理解:

简单来看下面这张图,大家更容易理解:

7

如上图所示,你总不想让你的项目到后期如图一样吧。(笑哭)

如上图所示,你总不想让你的项目到后期如图一样吧。(笑哭)

好了,下面Himi简单举个例子来说:

一: 首先大家创建一个C++的项目,然后请按照官方文档创建一个可移动的Actor。

链接: https://docs.unrealengine.com/latest/CHN/Programming/Tutorials/PlayerInput/index.html

二:在项目中创建一个蓝图类,并选择我们第一步创建好的C++ Actor (MyPawn),如下图:

2               3

Himi 这里将此蓝图命名为:“BP_MyPawn”

三:双击打开进行编辑BP_MyPawn蓝图,编辑其Event Graph如下图:

4

这里主要对此Actor进行了添加键盘Q和E的事件:

按键Q: 创建一个时间轴来循环打印一句话

按键E:利用(a+b)*c的公式来随机打印出一个数值

四:将第一步骤的C++ Actor 与 我们新建的蓝图Actor ,都放在世界中。

五:打开关卡蓝图,编辑Event Graph,如下图:

7

这里主要添加按键1,2来在C++ Actor与蓝图Actor 之间进行切换控制权。

六:效果图如下:(点击查看动态效果)

jdfw

按下键盘2,控制蓝图Actor,然后Q和E按键都有事件触发。

按下键盘1,切回到C++的Actor视角,按下Q和E没有任何事件触发。

          通过这个小例子,完成了C++与蓝图配合实现继承关系的过程!当然至于如何能够更好的配合使用,就交给各位了~ 发挥你的想象吧,希望本篇能为新童鞋解惑C++与蓝图。(PS. 其中很多蓝图的操作或者哪些看不懂的地方,那么请你先去好好撸一遍官方文档后,再来看:so easy!)

Himi 在UE4上也是个萌新,希望大家【加群 191452043】多多讨论共同进步!

2018-10-23 22:59:12 directx3d_beginner 阅读数 323
  • 从这里开始虚幻4-第2辑-蓝图 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2158 人正在学习 去看看 杨石兴

先列代码

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS()
class TESTUNREALC_API AMyActor : public AActor
{
 GENERATED_BODY()
 
public: 
 // Sets default values for this actor's properties
 AMyActor();

 UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = "Damage")
  int32 TotalDamage;

 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Damage")
  float DamageTimeInSeconds;

 UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Transient, Category = "Damage")
  float DamagePerSecond;

 UFUNCTION(BlueprintCallable, Category = "Damage")
  void CalculateValues();

 UFUNCTION(BlueprintImplementableEvent, Category = "DAMAGE")
  void CalledFromCpp();

 void CalledFromCpp_Implementation();
 // Called when the game starts or when spawned
 virtual void BeginPlay() override;
 
 // Called every frame
 virtual void Tick( float DeltaSeconds ) override;

 virtual void PostInitProperties() override;

 virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
 
 
};

// Fill out your copyright notice in the Description page of Project Settings.

#include "testunrealc.h"
#include "MyActor.h"


// Sets default values
AMyActor::AMyActor()
{
  // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
 PrimaryActorTick.bCanEverTick = true;
 TotalDamage = 200;
 DamageTimeInSeconds = 1.0f;
 DamagePerSecond = TotalDamage / DamageTimeInSeconds;

}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
 Super::BeginPlay();
 
}

// Called every frame
void AMyActor::Tick( float DeltaTime )
{
 Super::Tick( DeltaTime );

}

void AMyActor::PostInitProperties()
{
 Super::PostInitProperties();
 this->CalculateValues();
}

void AMyActor::CalculateValues()
{
 DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}

#if WITH_EDITOR
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
 this->CalculateValues();
 Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif

void AMyActor::CalledFromCpp_Implementation()
{

}

 

从此类中派生出两个蓝图类,其中一个蓝图类在编辑框的值能够随着输入值的变化而变化,一个不能随时计算

 

 

原因在于


void AMyActor::PostInitProperties()
{
 Super::PostInitProperties();
 this->CalculateValues();
}

#if WITH_EDITOR
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
 this->CalculateValues();
 Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif

2016-10-17 17:50:12 qq_20309931 阅读数 3367
  • 从这里开始虚幻4-第2辑-蓝图 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2158 人正在学习 去看看 杨石兴

Unreal Engine 4 C++ 动态加载UMG界面

最近研究了一下Unreal Engine4,本人不太习惯蓝图打开界面,就研究了下用C++来打开、显示界面。
好记性不如烂笔头啊,还是记录一下!

1.添加UMG依赖

在你的工程下找到以下文件 :

  • YourProject.Build.cs

添加如下代码:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG", "Slate", "SlateCore" });

添加后的完整文件:

  • YourProject.Build.cs
// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;

public class YourProject : ModuleRules
{
    public YourProject(TargetInfo Target)
    {
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG", "Slate", "SlateCore" });

        PrivateDependencyModuleNames.AddRange(new string[] {  });

        // Uncomment if you are using Slate UI
        // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");

        // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    }
}

在你的工程下找到以下文件 :

  • YourProject.h

添加如下代码:

#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "Runtime/UMG/Public/Slate/SObjectWidget.h"
#include "Runtime/UMG/Public/IUMGModule.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"

添加后的完整文件:

  • YourProject.Build.cs
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Engine.h"

#include "Runtime/UMG/Public/UMG.h"
#include "Runtime/UMG/Public/UMGStyle.h"
#include "Runtime/UMG/Public/Slate/SObjectWidget.h"
#include "Runtime/UMG/Public/IUMGModule.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"

2.创建一个界面

创建一个蓝图控件,取名为Test,如图:

创建蓝图控件

拖拽几个测试控件,如图:

编辑器界面


3.添加代码

  • YourGameMode.h
#pragma once

#include "GameFramework/GameMode.h"
#include "Blueprint/UserWidget.h"
#include "YourGameMode.generated.h"

/**
 * 
 */
UCLASS()
class YOURGAMEMODE_API AYourGameMode : public AGameMode
{
    GENERATED_BODY()

public:
    // 开始游戏
    virtual void StartPlay() override;

private:

    /** 在游戏开始时我们将作为菜单使用的控件类。 */
    UPROPERTY(EditAnywhere)
    TSubclassOf<UUserWidget> StartingWidgetClass;

    /** 用作为界面的实例。 */
    UPROPERTY(EditAnywhere)
    UUserWidget* CurrentWidget; 
};
  • YourGameMode.cpp
// Fill out your copyright notice in the Description page of Project Settings.

#include "YourGameProject.h"
#include "YourGameModeGameMode.h"

// 开始游戏
void AYourGameMode::StartPlay()
{
    Super::StartPlay();

    /** 游戏开始后弹出界面 */
    StartingWidgetClass = LoadClass<UUserWidget>(NULL, TEXT("Blueprint'/Game/UI/Test.Test_C'"));
    if (StartingWidgetClass != nullptr) 
    {
        CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), StartingWidgetClass);
        if (CurrentWidget != nullptr)
        {
            CurrentWidget->AddToViewport();
        }
    }
}

注意:这里有个大坑,动态加载UClass时需要在代码结尾增加“_C”

StartingWidgetClass = LoadClass<UUserWidget>(NULL, TEXT("Blueprint'/Game/UI/Test.Test_C'"));

4.编译测试

编译后可直接运行,如果成功显示出界面:

这里写图片描述

成功加载界面。

2016-12-03 11:59:37 qq_20309931 阅读数 3537
  • 从这里开始虚幻4-第2辑-蓝图 v4.18

    本课程系列取名英译系列,是录制人员参考国外英文原版经典教程,结合中国人的习惯录制而成。希望能够给大家以帮助。从这里开始虚幻4系列教程,是Unreal的官方发布的入门教学,非常经典,是学习Unreal的佳入口。

    2158 人正在学习 去看看 杨石兴

Unreal Engine 4 C++ Component介绍——WidgetComponent

好记性不如烂笔头啊,还是记录一下!


WidgetComponent简介

WidgetComponent是用来渲染UI的一种组件,可以被添加到ActorPawnCharacter上。在游戏中可以用来显示角色的头顶信息以及游戏场景中的公告板。

WidgetComponent可以渲染下列类型:

  • UMG的控件蓝图
  • 继承UUserWidget的控件
  • 继承SWidget的Slate控件

添加UMG依赖

首先,在你的工程中找到工程文件,找到以下行并添加UMG:

  • .Build.cs
PublicDependencyModuleNames.AddRange(
    new string[] {
        "Core",
        "CoreUObject",
        "Engine",
        "InputCore",
        "UMG"
    }
);

并取消以下行的注释:

// Uncomment if you are using Slate UI
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

修改后的完整文件如下:

// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;

public class YourProject : ModuleRules
{
    public YourProject(TargetInfo Target)
    {
        PublicDependencyModuleNames.AddRange(new string[] { 
            "Core", 
            "CoreUObject", 
            "Engine", 
            "InputCore", 
            "UMG"
            }
        );

        PrivateDependencyModuleNames.AddRange(new string[] {  });

        // Uncomment if you are using Slate UI
        PrivateDependencyModuleNames.AddRange(new string[] { 
            "Slate", 
            "SlateCore" 
            }
        );

        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");

        // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    }
}

UMG控件蓝图

1 创建控件蓝图

在工程中任意创建一个控件蓝图:

界面控件

接着在控件中的CanvasPanel中创建一个ProgressBar

ProgressBar

任意调整下细节设置,我的细节设置如下:

细节设置

一个蓝图控件就创建完成了,我们将控件命名为HpBar

2 添加到WidgetComponent

往任意Actor上添加一个新的WidgetComponent:

添加WidgetComponent

设置WidgetComponent如下图所示:

设置WidgetComponent

这里也有几个参数要讲一下:

  • Space 有两种方式WorldScreen

    World方式是绘制在场景中,会被物体遮挡,相当于一个场景的公告板。
    Screen方式是绘制在屏幕上,不会被物体遮挡,并且一直面向摄像机。

  • Widget Class 就是选择你要渲染的控件,选择刚才你创建的空间蓝图。

  • Draw Size 就是你要绘制的尺寸,根据你的需要调整。

调整完后我们来测试一下,就会发现控件已经正常显示了:

示例图片


UUserWidget的UMG控件

此方式需要使用C++

在你的Actor类中添加该属性

  • YourActor.h
// WidgetComponent
UPROPERTY(EditAnywhere, Category = WidgetComponent)
class UWidgetComponent* WidgetComponent;
  • YourActor.cpp
YourActor::YourActor(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{

    // ....你的代码

    // 初始化WidgetComponent
    WidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("WidgetComponent"));
    WidgetComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);

    // 设置控件蓝图
    UClass* Widget = LoadClass<UUserWidget>(NULL, TEXT("WidgetBlueprint'/Game/UI/widget/HpBar.HpBar_C'"));
    WidgetComponent->SetWidgetClass(Widget);

    // 设置User Widget
    WidgetComponent->SetWidgetClass(MyUserWidget::StaticClass());
}

这里值得注意的是SetWidgetClass这个方法。

  • 如果你需要加载的是控件蓝图

    就要像实例中使用LoadClass模板函数,加载Class后进行设置。

// 设置控件蓝图
UClass* Widget = LoadClass<UUserWidget>(NULL, TEXT("WidgetBlueprint'/Game/UI/widget/HpBar.HpBar_C'"));
WidgetComponent->SetWidgetClass(Widget);
  • 如果你需要加载的是一个继承自UUserWidget的C++类

    你可以像下面这样直接设置:

// 设置User Widget
WidgetComponent->SetWidgetClass(MyUserWidget::StaticClass());

SWidget的Slate控件

此方式需要使用C++

在你的Actor类中添加该属性,这里我用SProgressBar举例,原理相同,换成你想要的继承自SWidgetSlate控件就可以了,重写Slate

  • YourActor.h
// 在初始化完组件之后调用
virtual void PostInitializeComponents() override;

// Slate Widget
TSharedPtr<class SProgressBar> CurrentSlateWidget;
  • YourActor.cpp
void AMyCharacter::PostInitializeComponents()
{
    CurrentSlateWidget = SNew(SProgressBar);

    if ( CurrentSlateWidget.IsValid() )
    {
        WidgetComponent->SetSlateWidget(CurrentSlateWidget);
        // WidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);
    }
}

这里有个大坑,可能是WidgetComponent设计漏洞,只要用Slate Widget,就不能使用屏幕绘制模式(EWidgetSpace::Screen),使用就会崩溃。

如果用Slate制作的血条不能朝向摄像机,那真是太蛋疼了。
最后,我选择了旋转WidgetComponent的方式来解决这个问题。


扩展WidgetComponent

创建一个新类继承自WidgetComponent的新组建,取名为MyWidgetComponent

  • MyWidgetComponent.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Components/WidgetComponent.h"
#include "MyWidgetComponent.generated.h"

/**
 * 
 */
UCLASS(Blueprintable, ClassGroup="UserInterface", hidecategories=(Object,Activation,"Components|Activation",Sockets,Base,Lighting,LOD,Mesh), editinlinenew, meta=(BlueprintSpawnableComponent) )
class TESTMOBILE_API UMyWidgetComponent : public UWidgetComponent
{
    GENERATED_BODY()

public:
    // 设置该角色属性的默认值
    UMyWidgetComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;

protected:

    /** Should we Toward Camera */
    UPROPERTY(EditAnywhere, Category=UserInterface)
    bool bTowardCamera;
};

在类体重增加了一个是否朝向摄像机的属性,然后重写父类的TickComponent方法,实现如下:

  • MyWidgetComponent.h
// Fill out your copyright notice in the Description page of Project Settings.

#include "Kismet/GameplayStatics.h"
#include "MyWidgetComponent.h"


// 设置默认属性
UMyWidgetComponent::UMyWidgetComponent(const FObjectInitializer& ObjectInitializer /*= FObjectInitializer::Get()*/)
    : Super(ObjectInitializer)
    , bTowardCamera(true)
{
}


// Tick函数
void UMyWidgetComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    if (Space != EWidgetSpace::Screen  && bTowardCamera)
    {

        FRotator WidgetComponentRotator = GetComponentRotation();
        FRotator CameraRotator = UGameplayStatics::GetPlayerCameraManager(this, 0)->GetCameraRotation();

        this->SetWorldRotation(FRotator(-CameraRotator.Pitch, CameraRotator.Yaw+180, WidgetComponentRotator.Roll));

    }
}

TickComponent方法中,获取了当前摄像机的旋转信息,并根据旋转信息,并给MyWidgetComponent了一个朝向摄像机的旋转。

示例图片

现在,你的Slate控件可以朝向摄像机了。

关于方法传入的Pitch-Yaw—Roll可以参考Unreal Engine 4 C++ Camera Pitch Yaw Roll 直观理解
关于头顶血条的详细方法可以参考Unreal Engine 4 C++ UMG血条及头顶信息

搞定,这样就可以让Slate控件朝向摄像机。

没有更多推荐了,返回首页