斯坦福UE4 C++课学习补充18:十字准星

一、创建准心UI

  1. 调整摄像机位置:现在游戏角色位于镜头的正中间,这样会在操作中遮挡玩家的视线。 而通常在第三人称游戏中,人物通常位于画面的偏左或者偏右位置。
  2. 进入Player的蓝图编辑器,选择弹簧臂组件SpringArmComp,调整其中的“摄像机”属性。通过设置“长度”可以变化摄像机与角色的距离,设置“插槽偏移”从而在改变相机位置时保持弹簧臂碰撞检测的功能。
  3. 回到Player蓝图中,将准星添加到视口

二、调整发射代码

  1. 在我们之前角色的发射代码中,为了简单起见,是直接使用的GetActorRotation获得角色的旋转方向作为粒子的发射方向。但是我们现在添加了十字准星,要实现指哪打哪,得沿着玩家视角即Controller视角发射,所以首先需要将GetActorRotation替换为GetControllerRotation

为什么原来发射方向是相对于Actor,而现在可以相对于Controller

  • 我的理解是:既然我们做了准心,也就是我们希望实际落点是我们可以控制的,那它就需要Controller玩家输入决定,而我们从枪口(手中)发射出去东西,扔出去的方向可以上任意的(想想游戏中扔手榴弹),所以可以做到与Actor无关,由准心决定了落点
  1. GetControlRotation()GetViewRotation() 在这里是一样的,但实际它们有所不同:
    (1)GetControllerRotation通常从玩家的控制器获取旋转信息,通常与玩家的输入设备(如鼠标或游戏手柄)直接相关。这个旋转角度通常用于确定玩家意图面向的方向,无论其角色的视觉表示如何。(即使角色模型因动画或物理影响而朝向不同方向,控制器的旋转仍然保持玩家的输入方向。)
    (2)GetViewRotation则是从玩家的视角或摄像机视角获取旋转信息。在使用第三人称摄像机时GetViewRotation可能会与GetControllerRotation不同,因为摄像机可能会围绕角色旋转,提供不同的视角。
    (3)举例一:吃鸡游戏中,控制器输入控制玩家的行进方向,而拖动小眼睛改变视口方向可以观察四周的环境。
    (4)举例二:塔防或策略游戏,控制器决定场景中小单位的移动方向,而视口方向一直是俯视全场
    (5)举例三:在VR应用中,控制器方向和视角方向的区别尤为明显:
    ①控制器输入方向:控制器在物理空间中的实际方向,可能用于指向或交互。
    ②摄像机方向:与用户头部的方向一致,用户头部的转动直接改变视角,与手中控制器的方向可能完全不同。
  2. 问题一:正脸面向镜头,技能会打到自己
  • 解决办法:触发OnActorOverlap事件后,先判断是否是Instigator(自己)
void ASurMagicProjectile::OnActorOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	//避免攻击者被自己的粒子伤害
	if (OtherActor && OtherActor != GetInstigator()) {
		//获得AttributeComp
		USurAttributeComponent* AttributeComp = Cast<USurAttributeComponent>(OtherActor->GetComponentByClass(USurAttributeComponent::StaticClass()));
		// 再次判空,可能碰到的是墙壁、箱子等没有血量的物体
		if (AttributeComp) {
			// 魔法粒子造成20血量伤害
			AttributeComp->ApplyHealthChange(-20.0f);
			// 一旦造成伤害就销毁,避免穿过角色继续计算
			Destroy();
		}
	}
} 
  1. 问题二:只要发射位置不在屏幕中心(相机位置),落点就会有偏差

准心是什么,就是最后玩家希望子弹落点的位置。为打到它,枪口时可以任意旋转的这点不用太纠结。我们要的是指哪打哪的视觉效果。

  • 解决方案:所以在做是否可以击中目标的测试中,我们可以从相机位置出发,按照控制器方向(玩家视角)做射线检测,再根据落点结果,用向量减法计算枪口朝向作为投掷物生成方向分为两种情况:
    (1)可以碰撞到指定目标:重写玩家想命中的点为目标end
    (2)没有碰撞到目标:按照原来设定好的最远的end同样计算得到发射方向
    注意:因为涉及到射线检测,所以传入参数时要考虑想打到的目标是什么类型,否则可能会存在检测不到直接穿过的情况
void ASCharacter::SpawnProjectile(TSubclassOf<AActor> ClassToSpawn)
{
	if (ensureAlways(ClassToSpawn))
	{
		//FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");
		FVector HandLocation = GetMesh()->GetSocketLocation(HandSocketName);

		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		SpawnParams.Instigator = this;

		FCollisionShape Shape;
		Shape.SetSphere(20.0f);

		// Ignore Player
		FCollisionQueryParams Params;
		Params.AddIgnoredActor(this);

		FCollisionObjectQueryParams ObjParams;
		ObjParams.AddObjectTypesToQuery(ECC_WorldDynamic);
		ObjParams.AddObjectTypesToQuery(ECC_WorldStatic);
		ObjParams.AddObjectTypesToQuery(ECC_Pawn);

		FVector TraceStart = CameraComp->GetComponentLocation();

		// endpoint far into the look-at distance (not too far, still adjust somewhat towards crosshair on a miss)
		FVector TraceEnd = CameraComp->GetComponentLocation() + (GetControlRotation().Vector() * 5000);

		FHitResult Hit;
		// returns true if we got to a blocking hit
		if (GetWorld()->SweepSingleByObjectType(Hit, TraceStart, TraceEnd, FQuat::Identity, ObjParams, Shape, Params))
		{
			// Overwrite trace end with impact point in world
			TraceEnd = Hit.ImpactPoint;
		}

		// find new direction/rotation from Hand pointing to impact point in world.
		FRotator ProjRotation = FRotationMatrix::MakeFromX(TraceEnd - HandLocation).Rotator();

		FTransform SpawnTM = FTransform(ProjRotation, HandLocation);
		GetWorld()->SpawnActor<AActor>(ClassToSpawn, SpawnTM, SpawnParams);
	}
}
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值