[UE4入门笔记(13)] 40.准星 41.射线检测(续第12篇) 42.行为状态机 --梁迪老师UE4纯C++&Slate开发沙盒游戏

前言:

笔者目前在校本科大三,目标方向是人工智能、计算机视觉。上一个OpenCV学习笔记专栏已完结,在学习完OpenCV后,我继续学习C++,并用纯C++做UE4项目的方式继续提升自己的水平。

梁迪老师的水平非常高,他的课程本来也无需笔记:课程本身即为最好的笔记。但由于我天赋有限,还是边看边记,以防遗忘——知识点太多,步骤太繁杂了。在学习过程中,我也偶有思考,思索为什么某个方法老师要这样做。所以,一是为了记录,二是为了分享,才有了这个专栏。

内容方面,由于我在开启这个专栏时,此项目已经做完很多了。所以,前期的一些大篇幅叙述的知识,可能在后期应用中一带而过。以及,前期的一些知识,后期会重新剖析,并加上我的个人理解。

另外,若有学术交流/学业交流意愿,可以邮件联系1246210283@qq.com,希望一齐进步。


本篇学习内容:

40.准星
41.射线检测(续第12篇)
42.行为状态机


40.准星

实现目标:在屏幕中央有一个圆形白色准星,识别到物体时变为绿色,开始砍石头时变成一个大的圆形进度条。

(1)在UE4编辑器内新建一个材质,注意设置Material的Material domain为User Interface,Blend Mode设为Masked,这样材质可以附到笔刷上。

(2)设置好这个材质,随后创建C++文件来编辑这个准心

(3)创建一个SlateWidget,完成准备工作。

(4)写SlateWidget的结构:一个大小可变的SBox,里面有SImage绑定了创建的材质实例。同时,定义了一个指针指向材质实例,通过修改材质实例的参数来动态修改准星状态。

(5)修改材质实例的参数要绑定到PlayerController下:先在Playercontroller下声明一个委托并实例化,然后在GameHUD下绑定、调用(即ExcuteIfBound在PlayerController的Tick函数中)

41.射线检测(续第12篇)

此小节接续39小节。

实现目标:在玩家头部发出一道射线,一旦检测到了物品,就在游戏上方的显示框内显示物品的名字。

(1)在PlayerController下创建三个private方法,并实现。

	//射线检测结果
	FHitResult RayGetHitResult(FVector TraceStart, FVector TraceEnd);

	//射线绘制
	void DrawRayLine(FVector StartPos, FVector EndPos, float Duration);

	//进行射线检测
	void RunRayCast();

FHitResult ASlAiPlayerController::RayGetHitResult(FVector TraceStart, FVector TraceEnd)
{
	FCollisionQueryParams TraceParams(true); //定义射线检测的一些参数
	TraceParams.AddIgnoredActor(SPCharacter);
	TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = false; //是否返回物理材质
	TraceParams.bTraceComplex = true; //检测混合的碰撞

	FHitResult Hit(ForceInit);
	//GameTraceChannel1是我们在UE4编辑器内新建的Trace通道的第一个。
	if (GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECollisionChannel::ECC_GameTraceChannel1, TraceParams)) {
		DrawRayLine(TraceStart, TraceEnd, 5.f);
	}
	return Hit;
}

(2)在RunRayCast()内创建保存是否检测到物品的变量,和检测到物品的AActor。

	//是否检测到物品
	bool IsDetected = false;

	RayActor = Hit.GetActor();

(3)关于从检测到的物品获取其文字信息,在PickupObject内实现。在ResourceObject内也实现一个功能一样的同名函数。

	//获取物品信息
	FText GetInfoText() const;

(4)配合实现PlayerState下的GetRayInfoText()函数,这是之前定义的委托。
在.h中定义RayInfoText:

//射线检测信息Text,由PlayerController进行更新
	FText RayInfoText;

FText ASlAiPlayerState::GetRayInfoText() const
{
	return RayInfoText;
}

(5)完善RunRayCast()函数,如果RayActor转为ASlAiPickupObject或者ASlAiResourceObject成功,就修改游戏显示框内物品的名字。

void ASlAiPlayerController::RunRayCast()
{

	//初始化起始位置和结束位置
	FVector StartPos(0.f);
	FVector EndPos(0.f);

	switch (SPCharacter->GameView)
	{
	case EGameViewMode::First:
		StartPos = SPCharacter->FirstCamera->K2_GetComponentLocation();
		EndPos = StartPos + SPCharacter->FirstCamera->GetForwardVector() *2000.f;
		break;
	case EGameViewMode::Third:
		StartPos = SPCharacter->ThirdCamera->K2_GetComponentLocation();
		StartPos = StartPos + SPCharacter->ThirdCamera->GetForwardVector() * 300.f;
		EndPos = StartPos + SPCharacter->ThirdCamera->GetForwardVector() * 2000.f;
		break;
	}

	//是否检测到物品
	bool IsDetected = false;

	FHitResult Hit = RayGetHitResult(StartPos, EndPos);

	RayActor = Hit.GetActor();

	//更好的做法是创建一个基类,PickupObject和ResourceObject都是它的子类,然后实现一个虚函数GetInfoText()
	if (Cast<ASlAiPickupObject>(RayActor)) {
		//如果Cast成功
		IsDetected = true;
		SPState->RayInfoText = Cast<ASlAiPickupObject>(RayActor)->GetInfoText();
	}
	if (Cast<ASlAiResourceObject>(RayActor)) {
		IsDetected = true;
		SPState->RayInfoText = Cast<ASlAiResourceObject>(RayActor)->GetInfoText();
	}
	if (Cast<ASlAiEnemyCharacter>(RayActor)) {
		IsDetected = true;
		SPState->RayInfoText = Cast<ASlAiEnemyCharacter>(RayActor)->GetInfoText();
	}
	//如果什么都没有检测到那就设置信息为无
	if (!IsDetected) {
		SPState->RayInfoText = FText();
	}
}

42.行为状态机

实现目标:准星在检测到树木、石头时变为绿色,开始砍树时变大且变为进度条状态。在射线识别到可拾取物品时,右手动作要改为拾取。

(1)以上实现目标均由StateMachine()函数完成。为了完成这个函数,需要完善很多东西。

(2)在PlayerState中实现GetAffectRange()方法,用来获取手上物品的攻击距离。

(3)在ResourceObject中定义血量、基础血量,并在BeginPlay()中获取。

(4)在ResourceObject中实现获取资源类型、获取血量百分比、获取伤害函数。

(5)在PlayerState中实现获取伤害值函数,因为伤害值随手上物品类型和资源类型而变化。

(6)在PickupObject中实现被拾取函数。

(7)补充StateMachine()函数:

void ASlAiPlayerController::StateMachine()
{
	//如果动作比较多,比较复杂。更好的做法是写几个状态机的类,进行控制。
	//普通模式
	ChangePreUpperType(EUpperBody::None);
	if (!Cast<ASlAiResourceObject>(RayActor) && !Cast<ASlAiHandObject>(RayActor)) {
		//如果不是资源物品或可拾取物品,准星显示白色
		UpdatePointer.ExecuteIfBound(false, 1.f);
	}
	//如果检测到资源
	if (Cast<ASlAiResourceObject>(RayActor)) {
		//如果鼠标左键没有按下,准星锁定模式
		if (!IsLeftButtonDown) {
			UpdatePointer.ExecuteIfBound(false, 0.f);
		}
		//如果左键已经按下,并且目标和人物的距离小于攻击范围,就开始打这个目标
		if (IsLeftButtonDown && FVector::Distance(RayActor->GetActorLocation(), SPCharacter->GetActorLocation())< SPState->GetAffectRange())  {
			//获取实际伤害
			int Damage = SPState->GetDamageValue(Cast<ASlAiResourceObject>(RayActor)->GetResourceType());
			//让资源受到伤害并且获取剩余血量百分比
			float Range = Cast<ASlAiResourceObject>(RayActor)->TakeObjectDamage(Damage)->GetHPRange();
			//更新准星
			UpdatePointer.ExecuteIfBound(true, Range);
		}
	}
	//如果检测到可拾取物品,并且两者的距离小于300,就可以拾取
	if (Cast<ASlAiPickupObject>(RayActor) && FVector::Distance(RayActor->GetActorLocation() , SPCharacter->GetActorLocation()) < 300.f) {
		//改变右手预动作为拾取
		ChangePreUpperType(EUpperBody::PickUp);
		//修改准星锁定模式
		UpdatePointer.ExecuteIfBound(false, 0.f);
		//如果右键按下
		if (IsRightButtonDown) {
			//把物品捡起来
			Cast<ASlAiPickupObject>(RayActor)->TakePickup();
		}
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值