Firing and Moving Not Working Together But Work Separately

I built a modified movement system from the Twin-Stick Shooter template, and the movement works. In particular, I made it so that the player could move in any of the four cardinal directions relative to his facing with the left stick, and rotate with the right stick.

This aspect of the system works.

In addition, I modified the system so that the player could only shoot in front of him. This is where things get dicey.

Shooting while standing still works fine. However, shooting while moving or rotating is weird. If he starts moving and then shooting, things work as expected. However, if the player first shoots and then moves, the shooting stops as soon as movement begins.

Here is how the system is implemented:

Set up Input:


void ATimeShooterPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	check(InputComponent);

	// set up gameplay key bindings
	InputComponent->BindAxis(MoveForwardBinding);
	InputComponent->BindAxis(MoveRightBinding);
	InputComponent->BindAxis(RotateRightBinding);
	InputComponent->BindAction("FireGuns", IE_Repeat, this, &ATimeShooterPawn::FireShot);
}

Tick:



void ATimeShooterPawn::Tick(float DeltaSeconds)
{

	const float ForwardValue = GetInputAxisValue(MoveForwardBinding);
	const float RightValue = GetInputAxisValue(MoveRightBinding);
	const float RotateRightValue = GetInputAxisValue(RotateRightBinding);

	ATimeShooterPawn::MoveForward(ForwardValue);
	ATimeShooterPawn::MoveRight(RightValue);
	ATimeShooterPawn::RotateShip(RotateRightValue);
}

Move Forward (Move Right is similar):


void ATimeShooterPawn::MoveForward(float Value)
{

	if ((Controller != NULL) && (Value != 0))
	{
		FVector forwardVec = GetActorForwardVector();
		AddMovementInput(forwardVec, Value);
	}
}

Rotate ship:


void ATimeShooterPawn::RotateShip(float Value)
{
	if ((Controller != NULL) && (Value != 0))
	{
		AddControllerYawInput(Value);
	}
}

Fire:


void ATimeShooterPawn::FireShot()
{
	// If we it's ok to fire again
	if (bCanFire == true)
	{
		UE_LOG(LogTimeShooter, Warning, TEXT("Shooting..."));		
		FVector FireDirection = GetActorForwardVector();

		const FRotator FireRotation = FireDirection.Rotation();
		// Spawn projectile at an offset from this pawn
		
		const FVector SpawnLocation = GetActorLocation() + FireRotation.RotateVector(GunOffset);

		UWorld* const World = GetWorld();
		if (World != NULL)
		{
			// spawn the projectile
			World->SpawnActor<ATimeShooterProjectile>(SpawnLocation, FireRotation);
		}
...


I trimmed FireShot() because it’s the same as in the template except for the use of GetActorForwardVector() instead of a parameter (since I will only shoot straight forward).

Based on the UE_LOG warning, the FireShot() function does not seem to get called.

Okay, so the main problem with your code is that your virtual override of Tick() does not call its superclass Tick(). In the APawn class, which you likely used as your superclass, Tick is used to update the pawn’s InputComponent, much like how you implemented the movement. For an easy, cheap fix, change your Tick function as follows:



void ATimeShooterPawn::Tick(float DeltaSeconds)
{
        **Super::Tick(DeltaSeconds);**

	const float ForwardValue = GetInputAxisValue(MoveForwardBinding);
	const float RightValue = GetInputAxisValue(MoveRightBinding);
	const float RotateRightValue = GetInputAxisValue(RotateRightBinding);

	ATimeShooterPawn::MoveForward(ForwardValue);
	ATimeShooterPawn::MoveRight(RightValue);
	ATimeShooterPawn::RotateShip(RotateRightValue);
}

But I urge you to reconsider how you do player control inputs. All input components should be referenced by function pointers. I have a meeting right now to get to but I’ll edit this post in a half hour or so reflecting what you could to to improve the robustness of your code.

I rewrote the input bindings to use function pointers, as per your suggestion, and added a call to the superclass Tick() function. However, the bug still does not appear to have been resolved. Here is the revised code:

Input Bindings:


void ATimeShooterPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	check(InputComponent);

	// set up gameplay key bindings
	InputComponent->BindAxis(MoveForwardBinding, this, &ATimeShooterPawn::MoveForward);
	InputComponent->BindAxis(MoveRightBinding, this, &ATimeShooterPawn::MoveRight);
	InputComponent->BindAxis(RotateRightBinding, this, &ATimeShooterPawn::RotateShip);
	InputComponent->BindAction("FireGuns", IE_Repeat, this, &ATimeShooterPawn::FireShot);
}

The Tick() function currently consists of only the call to Super::Tick(DeltaSeconds);.

I imagine it has to do with you binding your fire key to IE_Repeat type events. The sequence of Repeat events apparently get interrupted by any new input.

I would try using two separate bindings, IE_Pressed and IE_Released instead, bound to a function to start shooting and to stop shooting. You could for example set a boolean to true/false in those functions and do the actual shooting in Tick. You could also consider starting a timer in your function to start shooting, and stopping that same timer in your function to stop shooting. Then you wouldn’t have to manually check the interval in your Tick function.

Thank you for the help.

Splitting the FireGuns binding into two separate bindings which flip a uint32 flag, having Tick() check it and then call FireShot() if true seems to have fixed the error. I didn’t want to use a Timer since I already have a timer in FireShot() (setting a limit on how often the player is allowed to shoot), and I wasn’t sure whether that would conflict. Still, it seems to work and have a stable framerate on my rather low-powered computer, so I’m not worried at this point.

Sorry I didn’t get back to you sooner about that. I’m glad NisshokuZK responded as well. Good luck!