2019-06-10 11:54:23 qq_37003027 阅读数 149
  • 从这里开始虚幻4-Editor介绍 v4.18

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

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

unreal引擎编译c++代码的时候,如果代码中有错误,引擎非常容易崩溃,崩溃后,经常无法再次成功打开。这时候需要以下操作才可以重新打开:
1.将vs中的错误代码注释掉。
2.打开项目文件夹的目录,将vs、binary、intermediate、saved删掉。
3.在目录下找到.uproject右键重新生成。

2015-05-20 23:42:44 shangguanwaner 阅读数 2965
  • 从这里开始虚幻4-Editor介绍 v4.18

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

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

AddOnScreenDebugMessage(),用于向屏幕输出调试消息,但使用不当会造成UE4Editor无法打开和崩溃的。

在初始化对象时,如果还没有创建Viewport,这个时候调用这个函数,编辑器必定会崩溃。所以一定要主意。一个小时不能打开编辑器,调试换来的教训。

2007-10-14 18:33:00 sunicdavy 阅读数 3360
  • 从这里开始虚幻4-Editor介绍 v4.18

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

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

原文:

http://udn.epicgames.com/Two/UnrealScriptLanguageReference.html

 
将游戏逻辑放在脚本这样的沙盒(Sandbox)中,保护环境客户端调用,防止程序崩溃
 
虚幻脚本是:
类似java的无指针,自动垃圾回收机制
比C++更高级的时间,状态,属性和网络
 
 
引擎程序员使用C++写功能模块,并使用脚本封装成脚本可访问对象方法,脚本程序员根据游戏逻辑需求书写逻辑代码,并暴露可供调试和设置的属性,通过编辑器可以很方便的编辑和修改这些属性
 
需要花费时间的做事情的函数叫做延时函数,类似Sleep FinishAnim MoveTo等。延时函数只能在状态函数中调用,而不能是普通函数。普通函数只有在延时函数挂起时才能被执行
 
虚幻只是模拟windows的线程概念而非照抄,windows线程效率很低。
 
所有虚幻的对象脚本执行都是并行的,对象独立的
 
所有对象都继承于Object
Actor(继承于Object)定义了一些列诸如可控制的移动,交互和影响的行为
Pawn(继承于Actor) 一个拥有高阶AI和玩家控制级别的对象
Class(继承于Object) 定义了一类物体(有别于C++的class)
 
类定义修饰符:
Native:
表示其被C++后台支持,如果你有一个类为native Robots,虚幻将会在磁盘上需找Robots.dll实现脚本功能,前提是,C++类必须提供IMPLEMENT_CLASS宏定义
 
Abstrat:
         抽象类,类似于C++的接口类,接口类本身不能被实例化,子类必须实现其方法方可实例化。在虚幻脚本中有一个Pawn类,其本身只有用类似Brute类才能被实例化
 
Guid(a,b,c,d):
         现阶段没有被使用,在以后支持COM时将会被添加到虚幻中
Transient:
         表明这个类的不必保存到磁盘,也就是说这种类并不是永久使用的,而是暂时的。类似的有Players,Windows
Config(section_name):
         一些有必要保存的变量,全局变量。一个类就是一个ini文件
 
虚幻脚本的name类型可以是函数,状态或者类等等的预定义名称,它和字符串的区别就在于:字符串可以动态修改,而name一次确定,不再修改
变量修饰符:
Input:可以被虚幻输入系统识别的变量,类似于将键盘或者手柄的输入映射于某个变量
 
Native:
         从C++装载或者保存的类型
 
虚幻只支持1维数组,多维数组请自行实现
 
函数修饰符
Static:
         与C++不同的是:虚幻脚本静态函数只能访问静态函数,一些变量的默认值,不能调用非静态,实例化了的函数。并且不能被重载
 
Singular:
         一种防止函数被递归调用的修饰。当你写一个Actor碰撞另外一个Actor的逻辑时,函数递归调用将会导致栈溢出。添加这个修饰符后,已经被调用的函数就不会被重复调用。
 
Latent:
         延时函数:在状态中调用一个延时函数,在过一定的tick后,该函数才会被返回
 
Iterator:
         定义一个函数为迭代器,用于使用foreach遍历一个actor list
 
Simulated:
         当一个actor是模拟代理(simulated proxy)或者独立代理(autonomous proxy)时决定一个函数在客户端运行
 
Operator:
         与C++类似
 
Event:
         只是一个编译器修饰,在使用unreal -make –h可以将脚本事件函数与C++连接代码对应起来。
 
脚本中的
event Touch( Actor Other )
{ ... }
 
在被列出源码为
void Touch(class AActor* Other)
{
    FName N("Touch",FNAME_Intrinsic);
    struct {class AActor* Other; } Parms;
    Parms.Other=Other;
    ProcessEvent(N,&Parms);
}
 
 
状态
虚幻脚本是唯一一个于语言层支持状态的语言
使用状态的好处:
         可以很方便的书写基于状态的函数(逻辑),一些函数和逻辑就可以被不同的状态共享,而其执行结果只是决定于actor实际在做什么。
         状态中可以使用延时函数,这是一种特殊类型的函数,C++和java都没有实现。例如:你可以实现以下逻辑:打开一个门,等待2秒后播放音效然后再打开另外一道门,释放一些怪,让它们打玩家,你可以以非常普通常见,线性的逻辑来书写这些代码。而具体的细节将有虚幻底层来实现
 
 
 
 
3种主要的延时函数
Sleep:
         暂停脚本执行一段时间,后继续执行
 
FinishAnim
         等待当前播放的动画完毕后继续执行。使用这个函数可以很方便的书写动画驱动的逻辑,AI逻辑就是严格的的动画驱动(有别于时间驱动),平滑的动画控制是一个AI系统的关键
 
FinishInterpolation
         等待当前茶致电动作完成后继续
 
 
状态的流程控制:
         使用Goto语句在不同的代码段间切换
 
auto state MyState
{
Begin:
     Log( "MyState has just begun!" );
     Sleep( 2.0 );
     Log( "MyState has finished sleeping" );
     goto Begin;
}
 
使用GotoState转入执行某个状态
 
 
实例
// This is the automatic state to execute.
auto state Idle
{
     // When touched by another actor…
     function Touch( actor Other )
     {
              log( "I was touched, so I’m going to Attacking" );
              GotoState( ‘Attacking’ );
              Log( "I have gone to the Attacking state" );
     }
Begin:
     log( "I am idle…" );
     sleep( 10 );
     goto ‘Begin’;
}
 
// Attacking state.
state Attacking
{
Begin:
     Log( "I am executing the attacking state code" );
     //...
}
 
运行结果
I am idle...
I am idle...
I am idle...
I was touched, so I’m going to Attacking
I have gone to the Attacking state
I am executing the attacking state code
 
 
 
状态继承和可视规则
 
// 父类
class MyParentClass expands Actor;
 
// 非状态函数
function MyInstanceFunction()
{
         log( "Executing MyInstanceFunction" );
}
 
// A 状态
state MyState
{
         // A 状态函数
         function MyStateFunction()
         {
                   Log( "Executing MyStateFunction" );
         }
// Begin标签
Begin:
         Log("Beginning MyState");
}
 
// 子类实现
class MyChildClass expands MyParentClass;
 
// 重载非状态函数
function MyInstanceFunction()
{
         Log( "Executing MyInstanceFunction in child class" );
}
 
// 重定义状态,并且重载函数
state MyState
{
         // 被重载的函数
         function MyStateFunction()
         {
                   Log( "Executing MyStateFunction" );
         }
// 标签被重载
Begin:
         Log( "Beginning MyState in MyChildClass" );
}
 
 
规则:
1.       如果对象是一个状态,无论被定义过多少次,最后重载的那个类将会被执行
2.       否则,按照重载次数,并且是非状态版本的函数被执行
 
状态可以同样使用expand来继承状态
// Base Attacking state.
state Attacking
{
        
}
 
// 上段攻击
state MeleeAttacking expands Attacking
{
        
}
 
//远距离攻击.
state RangeAttacking expands Attacking
{
        
}
 
 
使用ignores指定忽略的函数
state Retreating
{
         // 忽略以下函数的调用
         ignores Touch, UnTouch, MyFunction;
 
}
 
 
默认属性:
         默认属性可以在物体在编辑器或者创建的时候给出一些列的预设值。这是一种很好的可重入性表现和代码规范的标准
 
 
 
技术细节:
         虚幻的垃圾回收采用与JAVA 虚拟机类似的树跟随方式(tree-following),回收器使用Uobject的序列化功能递归的获取到哪些物体引用了其他的对象
 
         虚幻脚本是基于bytecode的,编译时,脚本将类似于java一样被编译为虚拟机代码。这使得其可以做到方便的跨平台
 
         虚幻脚本是2次pass,第一遍pass确定变量,状态和函数定义,第二遍编译为p-code。这种机制使得复杂的循环依靠的逻辑继承结构可以完整的编译并且链接
        
         可恢复的玩家状态:虚幻可以在游戏进行的任何时刻保存所有actor的状态和运行情况到磁盘,并在必要时恢复。因为其状态和函数执行只是运行于低端的虚拟机,运行结果只是依赖于栈数据,所以,保存栈数据是非常关键的
 
虚幻文件包:这是一种包含索引,序列化dump和对象细节的二进制格式包。其很类似于DLL,可以拥有一个引用其他包的列表。这样可以很方便的将预定义包放在Internet提供下载,已经下载的包将不会在下载以减少下载次数(manifest机制)
 
 
 
开发策略
C++脚本大约每秒能执行5千万条指令,虚幻脚本能执行250万条指令,相差20倍,整个游戏中,脚本执行需要花费CPU 5~10%的时间。使用虚幻脚本做一些你实际感兴趣的时间,或者需要自定义的部分。而不是将一些基础移动的代码重新书写一遍,物理系统可以为你做一切。
 
大量使用延时函数,如果依然按照时间驱动的传统方式书写这类逻辑将是低效的
 
代码书写中减少无限递归的出现:在Move命令执行中,actor将发生碰撞,然后调用Bump函数,如果在Bump中依然有Move指令将形成无限递归。无限递归和死循环是虚幻脚本不能很好解决的地方(所有语言都是-_-! Tim太谦虚)
 
创建和销毁actor将是服务器端一个非常耗费资源的操作,特别是在网络游戏中,销毁和销毁中需要发包,占用一定的网络带宽。
 
尽量使用面向对象特性。在写新功能时尽可能的重载已有函数和状态让代码清爽,方便修改,便于和其他人的成果集成。避免使用传统的C技巧,类似于使用switch判断actor的状态并执行不同的操作。这样的操作阻止你加入新的类和修改一些东西。
 
 
2018-04-15 21:31:44 ZJU_fish1996 阅读数 738
  • 从这里开始虚幻4-Editor介绍 v4.18

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

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

        最近刚开始接触unreal,  由于时间比较赶没有系统学习就开始做项目了。中途遇到的bug多之又多。

        遇到的其中一个bug是:一开始经过测试,模块A是好的,于是开始写和模块A关联不大的模块B,C,D……突然有一天,模块A开始不定期出现崩溃,经过断点发现是断在执行的tick函数里,一开始给某个成员对象绑定了一个函数,每帧都去访问这个绑定的函数,但是会突然取不到这个函数,就会崩溃。

        除了一开始初始化绑定函数外,整个项目没有地方修改过这个函数,浪费了差不多半天的时间确认不是自己误改了这个成员变量的绑定函数,能够想到的唯一可能性就是这个绑定函数被ue4的内存回收机制清理了,带着这种猜想,给这个函数加上了UFUNCTION()宏,果然报错就没有再出现了!

        因为项目涉及到场景的动态生成,之前自己写了个管理的类,由这个类来生成管理所有的对象,一直平安无事,刚刚又发生了空指针报错崩溃。这时候回去看了一下自己新建出来的actor数组,果然没有保护宏,给TArray加上了UPROPERTY()的宏,一切又回归正常了。

        总之如果不是组件化管理(AddToRoot)的常驻对象,都尽可能用宏包裹起来,告诉ue4这个东西不要随便收走了。否则随着项目复杂,这种问题会慢慢浮出水面。

2016-04-11 23:05:09 noahzuo 阅读数 5921
  • 从这里开始虚幻4-Editor介绍 v4.18

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

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

算到现在使用UE4大概有两年了吧,从它每月还收费19美金的时候用到现在4.13都出来了。这是一款很强大的引擎,因此我也总结了方方面面的一些经验,这篇博客会时时更新。


Editor Only

如果想要代码只在Editor下编译,需要如下操作:

 #if WITH_EDITOR

 //editor only code -- doesn't appear in final game

 #endif

Lightmass

  • Stationary的光照如何产生软阴影?
    开启Use Area Shadows for Stationary Lights。

Build Error

  • InputCoreTypes.h如果出现各种syntax error.
    在PCH中加入#include "Engine.h"即可。

Plugin

  • 如果你要构建一个Function Lib Plugin,记得将LoadingPhase设定为PreDefault.

Console Command

创建Console Command的方法:在GameMode中创建对应的函数,UFUNCTION的Category需要为ExecFunctions

创建无参数的Console Command

    UFUNCTION(Exec, Category = ExecFunctions)
    void DoSomething();

创建带参数的Console Command

    /*Function with one parameter*/
    UFUNCTION(Exec, Category = ExecFunctions)
    void DoSomethingElse(float param);

如此即可


Android

获得Env

auto env = FAndroidApplication::GetJavaEnv();

GNativeAndroidApp

调用extern struct android_app* GNativeAndroidApp;
记得需要加入

#include <android_native_app_glue.h>

JNI

记得在.cs文件中的PrivateIncludePaths加入:
"Runtime/Launch/Private"


锁帧

  • 直接修改引擎设置的方法:

在config/ConsoleVariables.ini中找到[Startup]

在其后加入:

t.MaxFPS=30
  • 针对项目的方法:
    在DefaultEngine.ini中查找[SystemSettings]的section,如果没有则新建一个,在其后加入:
t.MaxFPS=30

Log to screen

如果你想向屏幕上输出一些东西,可以使用如下代码:

GEngine->AddOnScreenDebugMessage(-1, -1, FColor::Red, TEXT("阿妹你看,上帝压狗! "));

Log Category

如果你想要定义并且使用自己的Log,那么你应该这么做:

// Decleare Log Category

// General Log
DECLARE_LOG_CATEGORY_EXTERN(YourLog, Log, All);

// Logging during game startup
DECLARE_LOG_CATEGORY_EXTERN(YourInit, Log, All);

// Logging for your AI system
DECLARE_LOG_CATEGORY_EXTERN(YourAI, Log, All);

// Logging for Critical Errors that must always be addressed
DECLARE_LOG_CATEGORY_EXTERN(YourCriticalErrors, Log, All);

// Define Log Category 
// General Log
DEFINE_LOG_CATEGORY(YourLog);

// Logging during game startup
DEFINE_LOG_CATEGORY(YourInit);

// Logging for your AI system
DEFINE_LOG_CATEGORY(YourAI);

// Logging for Critical Errors that must always be addressed
DEFINE_LOG_CATEGORY(YourCriticalErrors);

// Using UE_LOG
//"This is a message to yourself during runtime!"
UE_LOG(YourLog,Warning,TEXT("This is a message to yourself during runtime!"));

格式化的Log

Log Message

//"阿妹你看,上帝压狗!"
UE_LOG(YourLog,Warning,TEXT("阿妹你看,上帝压狗!"));

Log an FString

%s 字符串在Log中是使用TCHAR* 的, 所以我们要使用 *FString

//"阿妹你看,上帝压狗!"
UE_LOG(YourLog,Warning,TEXT("阿妹你看,上帝压%s!"), *TheDog->GetName() );

Log an Int

//"有了金坷垃,小麦亩产1800!"
UE_LOG(YourLog,Warning,TEXT("有了金坷垃,小麦亩产%d!"), 1800);

Log a Float

//"有了金坷垃,小麦亩产1800.0f!"
UE_LOG(YourLog,Warning,TEXT("有了金坷垃,小麦亩产%f!"), 1800.0f);

其余的关于Vector, Color, FName等都同理可以进行输出。

Current Camera

当前相机的获得可以通过两种方式:

  • 可以使用GetOwningPlayerController函数:
auto pc = GetOwningPlayerController();
auto *vt = pc->GetViewTarget();
ACameraActor* camera = Cast(vt);
if (camera) {
    //do stuff
}
  • 使用GetPlayerCameraManager函数
auto camera = UGameplayStatics::GetPlayerCameraManager(WorldContext, 0);

其中的WorldContext是世界上下文参数。


Enumeration in C++

UENUM ()
namespace EBattleState
{
        enum Type
        {
              CameraWander = 0 ,                       // The camera is wandering around.
              ChooseCharacter ,                        // Choose one character, and is going to choose location.
              CharacterMoving ,                        // The character is moving, player input is not allowed.
              Count ,
        };
}

TEnumAsByte BattleStateEnum;

Apex Destruction

  • Destruction Mesh在还未破碎的情况下,是没有碰撞的,如果要启用,需要在Level中选中该Actor,将Use Async Scene设为False:

Use Async Scene

如果这个值是灰色不可改变,那么需要在Edit->Project Settings->Physics->Simulation->Enable Async Scene设定为True

  • 破碎后产生的小Chunks是默认与WorldDynamic无碰撞的,如果需要其有碰撞,那么需要将Large Chunk Threshold设定为一个比较大的数字:
    Large Chunk Threshold

  • 千万不要进行缩放你的Destructible Mesh,会导致Chunks的碰撞计算出错。

  • UE4中只支持随机切片,如果要进行自定义的Destructible,你需要apex physx lab,非常酷的东西。

Animation&Rigging Tool

  • 如果你不幸在ART中遇到了“Parent of end effector must be a joint”的错误,那么需要检查一下在你的骨骼模型中是否有double system,比如说头发啊或者裙摆之类的东西。
  • 如果你在有布料的骨骼模型看到了一个奇怪的顶点,例如下面这个:
    Strange Vertice
    这种情况通常是你的Skinning出了问题,着重检查那些不该有蒙皮信息的骨骼。
  • 若是出现了类似于No object matches name: ik_upperarm_fk_matcher_l的问题,则有可能是Rigging的版本太老。使用最新的ART代码重新Build一次整个Rigging系统。

Class名称的前缀

  • Template classes are prefixed with the letter T.
  • Classes inheriting from UObject are prefixed with the letter U.
  • Classes inheriting from AActor are prefixed with the letter A.
  • Classes inheriting from SWidget are prefixed with the letter S.
  • Abstract interface classes are prefixed with the letter I.
  • Most other classes are typically prefixed with the letter F.

关于Unreal Engine 4的工作流程

  • 当你在给你的场景进行光照布局的时候,记得一定要把眼球自适应关掉!
  • UE4与Perforce简直是天生一对,我他娘的太喜欢这一对了。
  • 当你每导入一个人形骨骼模型的时候,记得一定要在 Retarget Manager 中进行骨骼的设定。这样可以确保动画的Retargeting正常工作,而且可以节省很多工作量。

Components

  • Components一个很方便的作用是可以任意挂载,我用它来设计技能模块非常方便。
  • Components在CPP中的初始化:
// Your .h file
class USphereComponent* Sphere;

// Your .cpp file
Sphere = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("SphereComp"));

Blueprint

  • Bind一个event之后,要记得在event上点右键,选择RefreshNode。
  • 只有在Event Graph中才能设定Timeline。
  • 你每在Level中更改了一个Actor的信息,都会重新调用一次Construction Script。
  • 想要摄像机Lag吗?在SpringArm中进行设定吧!

Interface

  • 在UE4的编程中,Interface非常重要。类之间只能进行单一继承,而针对于Interface则可以进行多继承。个人的经验中,它对于物品交互等的构建都非常方便。

  • 在C++中创建Interface
    最基本代码如下:

.h

#pragma once

#include "Interface.h"
#include "InterfaceXBoxEvent.generated.h"

/** Class needed to support InterfaceCast<IToStringInterface>(Object) */
UINTERFACE()
class UInterfaceXBoxEvent : public UInterface
{
    GENERATED_UINTERFACE_BODY()
};

class IInterfaceXBoxEvent
{
    GENERATED_IINTERFACE_BODY()

public:
    UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Activate")
    void XboxEvent_KillAI(EAIType::Type type);
};

.cpp

#include "MyGame.h"
#include "InterfaceXBoxEvent.h"

UInterfaceXBoxEvent::UInterfaceXBoxEvent(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{

}

C++

  • 避免Garbage Collection的做法:

使用UObjectBaseUtility::AddToRoot()可以达到该目的。

  • TMap的使用
TMap<int32, FString> FruitMap;

FruitMap.Add(5, TEXT("Banana"));
FruitMap.Add(2, TEXT("Grapefruit"));
FruitMap.Add(7, TEXT("Pineapple"));
// FruitMap == [
//  { Key: 5, Value: "Banana"     },
//  { Key: 2, Value: "Grapefruit" },
//  { Key: 7, Value: "Pineapple"  }
// ]

注意与TMultiMap的区别,TMap中的key都是唯一的,因此当插入一个重复键值时,原来的会被替换:

FruitMap.Add(2, TEXT("Pear"));
// FruitMap == [
//  { Key: 5, Value: "Banana"    },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" }
// ]

也可以使用Emplace函数来进行元素的替换或增加,这种方法可以避免临时变量的创建:

FruitMap.Emplace(3, TEXT("Orange"));
// FruitMap == [
//  { Key: 5, Value: "Banana"    },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 3, Value: "Orange"    }
// ]

可以使用FindOrAdd来进行查找键值的查找,如TMap中没有这个键值,那么则会创建一个默认的值:

FString& Ref7 = FruitMap.FindOrAdd(7);
// Ref7     == "Pineapple"
// FruitMap == [
//  { Key: 5, Value: "Mango"     },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 3, Value: "Orange"    }
// ]
FString& Ref8 = FruitMap.FindOrAdd(8);
// Ref8     == ""
// FruitMap == [
//  { Key: 5, Value: "Mango"     },
//  { Key: 2, Value: "Pear"      },
//  { Key: 7, Value: "Pineapple" },
//  { Key: 3, Value: "Orange"    },
//  { Key: 8, Value: ""          }
// ]

可以使用Remove函数,RemoveAndCopyValue函数或者FindAndRemoveChecked函数来进行元素的删除。

我去……关于TMap都可以单独出一个博客了……

  • 在C++中寻找BP中的物件或类:
static ConstructorHelpers:: FObjectFinder<UStaticMesh > CubeMesh (TEXT( "StaticMesh'Content/TopDownBP/CubeMesh'" ));

if ( CubeMesh.Object )
{
    Mesh ->SetStaticMesh (CubeMesh. Object );
}
  • GetGlobalShaderMap如何使用?
const auto FeatureLevel = GMaxRHIFeatureLevel;
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
  • 默认材质
if(Material == NULL)
{
    Material = UMaterial:: GetDefaultMaterial(MD_Surface );
}
  • 在C++中调用Blueprint的函数

先吐槽,这个时候其实建议使用Interface来进行调用会清晰的多,以下方式只是Trick……


// MainPlayerCharacter.cpp
// By: Noah Zuo
// Disc: Call functions in a blueprint from C++

#include "MainPlayerCharacter.h"

AMainPlayerCharacter::AMainPlayerCharacter (const class FObjectInitializer& PCIP)
: Super( PCIP)
{
    // The BP is located at /Game/Blueprints/TestTest folder. 
    static ConstructorHelpers ::FObjectFinder<UBlueprint> assetObject(TEXT( "Blueprint'/Game/Blueprints/TestTest'" ));

    if (assetObject.Succeeded())
    {
        TestBlueprint = ( UClass*)assetObject .Object-> GeneratedClass;
    }
}

void AMainPlayerCharacter::BeginPlay()
{
    // Spawn a Actor in the world. 
    TestObjectActor = GWorld->SpawnActor<AActor >(TestBlueprint);
}


void AMainPlayerCharacter::Tick(float DeltaSeconds)
{
    Super::Tick (DeltaSeconds);

    UFunction *tmp = TestObjectActor->FindFunction(TEXT ("TestPrint"));

    if (tmp != NULL)
        TestObjectActor ->ProcessEvent(tmp, nullptr);
}

Particle

  • 如果想在BP/C++中动态调整Particle的参数,需要添加Dynamic模块。然后将Distribution设定为ParticleParameter。Param Name设定为Material Editor中的名字,Parameter Name设定为BP中的名字。
    Particle Dynamic Value
没有更多推荐了,返回首页