示例#1
0
void AFournoidWeapon::FireBullet()
{
	if ( BulletClass )
	{
		if ( MyPawn )
		{
			const auto SpawnRotation = MyPawn->GetControlRotation();
			const auto SpawnLocation = GetMuzzleLocation();
			const auto World = GetWorld();
			
			if ( World )
			{
				if ( Role == ROLE_Authority )
				{
					if ( !IsClipEmpty() )
					{
        				auto SpawnedBullet = World->SpawnActor<AFournoidBullet>(BulletClass, SpawnLocation, SpawnRotation);
        				SpawnedBullet->Instigator = Instigator;
        				PlayShootingFX();
    					CurrentClipSize -= 1;
            			GetWorldTimerManager().SetTimer(TimerHandle_HandleFireBullet, this, &AFournoidWeapon::FireBullet, FiringRate, false);
					}
					else
					{
						StartReloading();
					}
				}
			}
		}
	}
}
void ASWeaponInstant::FireWeapon()
{
    const FVector AimDir = GetAdjustedAim();
    const FVector CameraPos = GetCameraDamageStartLocation(AimDir);
    const FVector EndPos = CameraPos + (AimDir * WeaponRange);

    /* Check for impact by tracing from the camera position */
    FHitResult Impact = WeaponTrace(CameraPos, EndPos);

    const FVector MuzzleOrigin = GetMuzzleLocation();

    FVector AdjustedAimDir = AimDir;
    if (Impact.bBlockingHit)
    {
        /* Adjust the shoot direction to hit at the crosshair. */
        AdjustedAimDir = (Impact.ImpactPoint - MuzzleOrigin).GetSafeNormal();

        /* Re-trace with the new aim direction coming out of the weapon muzzle */
        Impact = WeaponTrace(MuzzleOrigin, MuzzleOrigin + (AdjustedAimDir * WeaponRange));
    }
    else
    {
        /* Use the maximum distance as the adjust direction */
        Impact.ImpactPoint = FVector_NetQuantize(EndPos);
    }

    ProcessInstantHit(Impact, MuzzleOrigin, AdjustedAimDir);
}
void ASWeaponInstant::ServerNotifyHit_Implementation(const FHitResult Impact, FVector_NetQuantizeNormal ShootDir)
{
    // If we have an instigator, calculate the dot between the view and the shot
    if (Instigator && (Impact.GetActor() || Impact.bBlockingHit))
    {
        const FVector Origin = GetMuzzleLocation();
        const FVector ViewDir = (Impact.Location - Origin).GetSafeNormal();

        const float ViewDotHitDir = FVector::DotProduct(Instigator->GetViewRotation().Vector(), ViewDir);
        if (ViewDotHitDir > AllowedViewDotHitDir)
        {
            // TODO: Check for weapon state

            if (Impact.GetActor() == nullptr)
            {
                if (Impact.bBlockingHit)
                {
                    ProcessInstantHitConfirmed(Impact, Origin, ShootDir);
                }
            }
            // Assume it told the truth about static things because we don't move and the hit
            // usually doesn't have significant gameplay implications
            else if (Impact.GetActor()->IsRootComponentStatic() || Impact.GetActor()->IsRootComponentStationary())
            {
                ProcessInstantHitConfirmed(Impact, Origin, ShootDir);
            }
            else
            {
                const FBox HitBox = Impact.GetActor()->GetComponentsBoundingBox();

                FVector BoxExtent = 0.5 * (HitBox.Max - HitBox.Min);
                BoxExtent *= ClientSideHitLeeway;

                BoxExtent.X = FMath::Max(20.0f, BoxExtent.X);
                BoxExtent.Y = FMath::Max(20.0f, BoxExtent.Y);
                BoxExtent.Z = FMath::Max(20.0f, BoxExtent.Z);

                const FVector BoxCenter = (HitBox.Min + HitBox.Max) * 0.5;

                // If we are within client tolerance
                if (FMath::Abs(Impact.Location.Z - BoxCenter.Z) < BoxExtent.Z &&
                        FMath::Abs(Impact.Location.X - BoxCenter.X) < BoxExtent.X &&
                        FMath::Abs(Impact.Location.Y - BoxCenter.Y) < BoxExtent.Y)
                {
                    ProcessInstantHitConfirmed(Impact, Origin, ShootDir);
                }
            }
        }
    }

    // TODO: UE_LOG on failures & rejection
}
void AAmethystWeapon_Projectile::FireWeapon()
{
    FVector ShootDir = GetAdjustedAim();
    FVector Origin = GetMuzzleLocation();
    
    // trace from camera to check what's under crosshair
    const float ProjectileAdjustRange = 10000.0f;
    const FVector StartTrace = GetCameraDamageStartLocation(ShootDir);
    const FVector EndTrace = StartTrace + ShootDir * ProjectileAdjustRange;
    FHitResult Impact = WeaponTrace(StartTrace, EndTrace);
    
    // and adjust directions to hit that actor
    if (Impact.bBlockingHit)
    {
        const FVector AdjustedDir = (Impact.ImpactPoint - Origin).SafeNormal();
        bool bWeaponPenetration = false;
        
        const float DirectionDot = FVector::DotProduct(AdjustedDir, ShootDir);
        if (DirectionDot < 0.0f)
        {
            // shooting backwards = weapon is penetrating
            bWeaponPenetration = true;
        }
        else if (DirectionDot < 0.5f)
        {
            // check for weapon penetration if angle difference is big enough
            // raycast along weapon mesh to check if there's blocking hit
            
            FVector MuzzleStartTrace = Origin - GetMuzzleDirection() * 150.0f;
            FVector MuzzleEndTrace = Origin;
            FHitResult MuzzleImpact = WeaponTrace(MuzzleStartTrace, MuzzleEndTrace);
            
            if (MuzzleImpact.bBlockingHit)
            {
                bWeaponPenetration = true;
            }
        }
        
        if (bWeaponPenetration)
        {
            // spawn at crosshair position
            Origin = Impact.ImpactPoint - ShootDir * 10.0f;
        }
        else
        {
            // adjust direction to hit
            ShootDir = AdjustedDir;
        }
    }
    
    ServerFireProjectile(Origin, ShootDir);
}
void ASWeaponInstant::ServerNotifyMiss_Implementation(FVector_NetQuantizeNormal ShootDir)
{
    const FVector Origin = GetMuzzleLocation();
    const FVector EndTrace = Origin + (ShootDir * WeaponRange);

    // Play on remote clients
    HitImpactNotify = EndTrace;

    if (GetNetMode() != NM_DedicatedServer)
    {
        SpawnTrailEffects(EndTrace);
    }
}
void AShooterWeapon_Instant::SpawnTrailEffect(const FVector& EndPoint)
{
    if (TrailFX)
    {
        const FVector Origin = GetMuzzleLocation();

        UParticleSystemComponent* TrailPSC = UGameplayStatics::SpawnEmitterAtLocation(this, TrailFX, Origin);
        if (TrailPSC)
        {
            TrailPSC->SetVectorParameter(TrailTargetParam, EndPoint);
        }
    }
}
示例#7
0
void AFournoidWeapon::PlayFireAnimation()
{
	if ( FireAnimation )
	{
		auto AnimInstance = Mesh1P->GetAnimInstance();
		if ( AnimInstance )
		{
			AnimInstance->Montage_Play(FireAnimation, 1.f);
		}
	}
	
	if ( FireEmitter )
	{
		UGameplayStatics::SpawnEmitterAtLocation(this, FireEmitter, GetMuzzleLocation());
	}
}
void AShooterWeapon_Instant::ServerNotifyMiss_Implementation(FVector_NetQuantizeNormal ShootDir, int32 RandomSeed, float ReticleSpread)
{
    const FVector Origin = GetMuzzleLocation();

    // play FX on remote clients
    HitNotify.Origin = Origin;
    HitNotify.RandomSeed = RandomSeed;
    HitNotify.ReticleSpread = ReticleSpread;

    // play FX locally
    if (GetNetMode() != NM_DedicatedServer)
    {
        const FVector EndTrace = Origin + ShootDir * InstantConfig.WeaponRange;
        SpawnTrailEffect(EndTrace);
    }
}
void ASWeaponInstant::SimulateInstantHit(const FVector& ImpactPoint)
{
    const FVector MuzzleOrigin = GetMuzzleLocation();

    /* Adjust direction based on desired crosshair impact point and muzzle location */
    const FVector AimDir = (ImpactPoint - MuzzleOrigin).GetSafeNormal();

    const FVector EndTrace = MuzzleOrigin + (AimDir * WeaponRange);
    const FHitResult Impact = WeaponTrace(MuzzleOrigin, EndTrace);

    if (Impact.bBlockingHit)
    {
        SpawnImpactEffects(Impact);
        SpawnTrailEffects(Impact.ImpactPoint);
    }
    else
    {
        SpawnTrailEffects(EndTrace);
    }
}
void ASWeaponInstant::SpawnTrailEffects(const FVector& EndPoint)
{
    // Keep local count for effects
    BulletsShotCount++;

    const FVector Origin = GetMuzzleLocation();
    FVector ShootDir = EndPoint - Origin;

    // Only spawn if a minimum distance is satisfied.
    if (ShootDir.Size() < MinimumProjectileSpawnDistance)
    {
        return;
    }

    if (BulletsShotCount % TracerRoundInterval == 0)
    {
        if (TracerFX)
        {
            ShootDir.Normalize();
            UGameplayStatics::SpawnEmitterAtLocation(this, TracerFX, Origin, ShootDir.Rotation());
        }
    }
    else
    {
        // Only create trails FX by other players.
        ASCharacter* OwningPawn = GetPawnOwner();
        if (OwningPawn && OwningPawn->IsLocallyControlled())
        {
            return;
        }

        if (TrailFX)
        {
            UParticleSystemComponent* TrailPSC = UGameplayStatics::SpawnEmitterAtLocation(this, TrailFX, Origin);
            if (TrailPSC)
            {
                TrailPSC->SetVectorParameter(TrailTargetParam, EndPoint);
            }
        }
    }
}
FVector AShooterWeapon::GetCameraDamageStartLocation(const FVector& AimDir) const
{
	AShooterPlayerController* PC = MyPawn ? Cast<AShooterPlayerController>(MyPawn->Controller) : NULL;
	AShooterAIController* AIPC = MyPawn ? Cast<AShooterAIController>(MyPawn->Controller) : NULL;
	FVector OutStartTrace = FVector::ZeroVector;

	if (PC)
	{
		// use player's camera
		FRotator UnusedRot;
		PC->GetPlayerViewPoint(OutStartTrace, UnusedRot);

		// Adjust trace so there is nothing blocking the ray between the camera and the pawn, and calculate distance from adjusted start
		OutStartTrace = OutStartTrace + AimDir * ((Instigator->GetActorLocation() - OutStartTrace) | AimDir);
	}
	else if (AIPC)
	{
		OutStartTrace = GetMuzzleLocation();
	}

	return OutStartTrace;
}
void AShooterWeapon_Instant::ServerNotifyHit_Implementation(const FHitResult Impact, FVector_NetQuantizeNormal ShootDir, int32 RandomSeed, float ReticleSpread)
{
    const float WeaponAngleDot = FMath::Abs(FMath::Sin(ReticleSpread * PI / 180.f));

    // if we have an instigator, calculate dot between the view and the shot
    if (Instigator && (Impact.GetActor() || Impact.bBlockingHit))
    {
        const FVector Origin = GetMuzzleLocation();
        const FVector ViewDir = (Impact.Location - Origin).GetSafeNormal();

        // is the angle between the hit and the view within allowed limits (limit + weapon max angle)
        const float ViewDotHitDir = FVector::DotProduct(Instigator->GetViewRotation().Vector(), ViewDir);
        if (ViewDotHitDir > InstantConfig.AllowedViewDotHitDir - WeaponAngleDot)
        {
            if (CurrentState != EWeaponState::Idle)
            {
                if (Impact.GetActor() == NULL)
                {
                    if (Impact.bBlockingHit)
                    {
                        ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);
                    }
                }
                // assume it told the truth about static things because the don't move and the hit
                // usually doesn't have significant gameplay implications
                else if (Impact.GetActor()->IsRootComponentStatic() || Impact.GetActor()->IsRootComponentStationary())
                {
                    ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);
                }
                else
                {
                    // Get the component bounding box
                    const FBox HitBox = Impact.GetActor()->GetComponentsBoundingBox();

                    // calculate the box extent, and increase by a leeway
                    FVector BoxExtent = 0.5 * (HitBox.Max - HitBox.Min);
                    BoxExtent *= InstantConfig.ClientSideHitLeeway;

                    // avoid precision errors with really thin objects
                    BoxExtent.X = FMath::Max(20.0f, BoxExtent.X);
                    BoxExtent.Y = FMath::Max(20.0f, BoxExtent.Y);
                    BoxExtent.Z = FMath::Max(20.0f, BoxExtent.Z);

                    // Get the box center
                    const FVector BoxCenter = (HitBox.Min + HitBox.Max) * 0.5;

                    // if we are within client tolerance
                    if (FMath::Abs(Impact.Location.Z - BoxCenter.Z) < BoxExtent.Z &&
                            FMath::Abs(Impact.Location.X - BoxCenter.X) < BoxExtent.X &&
                            FMath::Abs(Impact.Location.Y - BoxCenter.Y) < BoxExtent.Y)
                    {
                        ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);
                    }
                    else
                    {
                        UE_LOG(LogShooterWeapon, Log, TEXT("%s Rejected client side hit of %s (outside bounding box tolerance)"), *GetNameSafe(this), *GetNameSafe(Impact.GetActor()));
                    }
                }
            }
        }
        else if (ViewDotHitDir <= InstantConfig.AllowedViewDotHitDir)
        {
            UE_LOG(LogShooterWeapon, Log, TEXT("%s Rejected client side hit of %s (facing too far from the hit direction)"), *GetNameSafe(this), *GetNameSafe(Impact.GetActor()));
        }
        else
        {
            UE_LOG(LogShooterWeapon, Log, TEXT("%s Rejected client side hit of %s"), *GetNameSafe(this), *GetNameSafe(Impact.GetActor()));
        }
    }
}