diff --git a/Game/GigLine/Content/Input/IA_Move.uasset b/Game/GigLine/Content/Input/IA_Move.uasset index 132b853..99ea36d 100644 --- a/Game/GigLine/Content/Input/IA_Move.uasset +++ b/Game/GigLine/Content/Input/IA_Move.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4278563b2653917e011eac249c4400c7e0874a59374afede5b7d0ca665a81916 -size 1306 +oid sha256:346f83e78be0e76f39c4cc0a4aa39fa783ed77f070001510f094fba3d79e45a4 +size 1110 diff --git a/Game/GigLine/Content/Input/IA_MoveBack.uasset b/Game/GigLine/Content/Input/IA_MoveBack.uasset index 26c4977..6c3fe87 100644 --- a/Game/GigLine/Content/Input/IA_MoveBack.uasset +++ b/Game/GigLine/Content/Input/IA_MoveBack.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88c9f7881e8a91c11b60f84af3538e74fc5e94c2731211b40b24159d7b42f137 -size 1322 +oid sha256:0af8585fb7f572e80625915845d4efc940629586431f95d04b8e695661720f43 +size 1126 diff --git a/Game/GigLine/Content/Input/IA_Strafe.uasset b/Game/GigLine/Content/Input/IA_Strafe.uasset index 1f88636..4b16d43 100644 --- a/Game/GigLine/Content/Input/IA_Strafe.uasset +++ b/Game/GigLine/Content/Input/IA_Strafe.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a30d908d7c055af0e41d0d6900c19bacd218387db36bab281895a8802936c992 -size 1314 +oid sha256:79c1877d7f84218179ae68b6ceb0bd9ab68623e3ecf617f733c65f9364484362 +size 1118 diff --git a/Game/GigLine/Content/Input/IA_StrafeLeft.uasset b/Game/GigLine/Content/Input/IA_StrafeLeft.uasset index 83f7a34..0c6fd72 100644 --- a/Game/GigLine/Content/Input/IA_StrafeLeft.uasset +++ b/Game/GigLine/Content/Input/IA_StrafeLeft.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aeaad125209e4c80921ac4e674a0aeb0b12bf9240b4d0110a74583bb04eb7810 -size 1330 +oid sha256:23b0fdb4a5d3cef749b89acb50c45fe7e6a824b5d29db3de6163f81e98d53dda +size 1134 diff --git a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp index ee85116..13a768f 100644 --- a/Game/GigLine/Source/GigLine/GigLineCharacter.cpp +++ b/Game/GigLine/Source/GigLine/GigLineCharacter.cpp @@ -7,9 +7,7 @@ #include "GameFramework/CharacterMovementComponent.h" #include "GameFramework/PlayerController.h" #include "InputAction.h" -#include "GameFramework/PlayerController.h" #include "InputMappingContext.h" -#include "Components/CapsuleComponent.h" #include "Engine/World.h" #include "TimerManager.h" @@ -89,9 +87,9 @@ AGigLineCharacter::AGigLineCharacter() void AGigLineCharacter::LoadInputAssets() { - // The assets are hard-referenced and loaded in the constructor; this deferred - // call pushes the now-valid mapping context to the local player, after the - // pawn has been possessed and its PlayerController exists. + // The assets are hard-referenced and loaded in the constructor. This pushes + // the now-valid mapping context to the local player's subsystem; called from + // SetupPlayerInputComponent at possession, when the controller exists. if (MappingContext) { if (const APlayerController* PC = Cast(GetController())) @@ -100,7 +98,17 @@ void AGigLineCharacter::LoadInputAssets() ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) { Subsystem->AddMappingContext(MappingContext, 0); + UE_LOG(LogTemp, Log, TEXT("GigLine IN: pushed mapping context %s"), + *MappingContext->GetName()); } + else + { + UE_LOG(LogTemp, Log, TEXT("GigLine IN: no enhanced input subsystem on local player")); + } + } + else + { + UE_LOG(LogTemp, Log, TEXT("GigLine IN: no PlayerController at LoadInputAssets")); } } } @@ -112,11 +120,39 @@ void AGigLineCharacter::BeginPlay() StartLocation = GetActorLocation(); StartRotation = GetActorRotation(); - // Input assets are hard-referenced in the constructor. Defer the mapping - // context push to just after spawn, when the PlayerController exists and a - // packaged game has finished mounting its pak IoStore. - FTimerHandle InputHandle; - GetWorldTimerManager().SetTimer(InputHandle, this, &AGigLineCharacter::LoadInputAssets, 1.0f, false); + // Force game input mode + focus + mouse capture so keyboard/mouse reach the + // pawn regardless of window focus quirks. + if (APlayerController* PC = Cast(GetController())) + { + PC->SetInputMode(FInputModeGameOnly()); + PC->bShowMouseCursor = false; + } + + // TEMP QA: one screenshot at +1.5s so the actual frame is reviewable. + FTimerHandle ShotHandle; + GetWorldTimerManager().SetTimer(ShotHandle, [this]() + { + if (APlayerController* PC = Cast(GetController())) + { + PC->ConsoleCommand(TEXT("HighResShot 1280 720")); + } + }, 1.5f, false); + + // TEMP QA: every second log world time + capsule Z + gravity + fall state so + // we can see whether the world is simulating the pawn at all (symptom: + // "spawns above ground but never falls, no input"). + FTimerHandle SimHandle; + GetWorldTimerManager().SetTimer(SimHandle, [this]() + { + UCharacterMovementComponent* M = GetCharacterMovement(); + UE_LOG(LogTemp, Log, + TEXT("GigLine SIM: worldTime=%.2f Z=%.0f grav=%.1f fall=%d pawn=%s"), + GetWorld() ? GetWorld()->GetTimeSeconds() : -1.f, + GetActorLocation().Z, + M ? M->GetGravityZ() : 0.f, + (M && M->IsFalling()) ? 1 : 0, + *GetName()); + }, 1.0f, true); } void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) @@ -130,6 +166,10 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo return; } + // Push the mapping context for this controller's local player so the bound + // actions receive input (runs at possession, when the controller exists). + LoadInputAssets(); + // One action per direction keeps the sign explicit: W and S differ only by // which handler runs, not by a modifier in the asset. if (MoveAction) @@ -165,6 +205,12 @@ void AGigLineCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCo Input->BindAction(JumpAction, ETriggerEvent::Started, this, &AGigLineCharacter::StartJump); Input->BindAction(JumpAction, ETriggerEvent::Completed, this, &AGigLineCharacter::StopJump); } + UE_LOG(LogTemp, Log, TEXT("GigLine IN: bound %d actions; ctx=%s move=%s look=%s jmp=%s"), + (Input ? 1 : 0), + MappingContext ? TEXT("ok") : TEXT("nil"), + MoveAction ? TEXT("ok") : TEXT("nil"), + LookAction ? TEXT("ok") : TEXT("nil"), + JumpAction ? TEXT("ok") : TEXT("nil")); } namespace @@ -191,6 +237,8 @@ namespace void AGigLineCharacter::MoveForward(const FInputActionValue& Value) { + UE_LOG(LogTemp, Log, TEXT("GigLine IN: MoveForward fired, axis=%.3f type=%d"), + FMath::Abs(SignedAxis(Value, false)), (int)Value.GetValueType()); ApplyMove(FMath::Abs(SignedAxis(Value, false)), 0.0f); } @@ -212,11 +260,16 @@ void AGigLineCharacter::MoveLeft(const FInputActionValue& Value) void AGigLineCharacter::ApplyMove(float Forward, float Strafe) { const AController* Controller = GetController(); - if (!Controller || (FMath::IsNearlyZero(Forward) && FMath::IsNearlyZero(Strafe))) + if (!Controller) + { + UE_LOG(LogTemp, Log, TEXT("GigLine IN: ApplyMove called but NO controller (fwd %.2f strafe %.2f)"), + Forward, Strafe); + return; + } + if (FMath::IsNearlyZero(Forward) && FMath::IsNearlyZero(Strafe)) { return; } - // Forward/right on the controller yaw keeps the capsule upright on slopes. const FRotator YawRotation(0.0f, Controller->GetControlRotation().Yaw, 0.0f); const FVector ForwardDir = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); @@ -224,6 +277,8 @@ void AGigLineCharacter::ApplyMove(float Forward, float Strafe) AddMovementInput(ForwardDir, Forward); AddMovementInput(RightDir, Strafe); + UE_LOG(LogTemp, Log, TEXT("GigLine IN: ApplyMove fwd %.2f strafe %.2f controller=%s yaw=%.0f"), + Forward, Strafe, *Controller->GetName(), Controller->GetControlRotation().Yaw); } void AGigLineCharacter::Look(const FInputActionValue& Value) diff --git a/Game/GigLine/Source/GigLine/GigLineGameModeBase.cpp b/Game/GigLine/Source/GigLine/GigLineGameModeBase.cpp index 712292d..dd6bd6f 100644 --- a/Game/GigLine/Source/GigLine/GigLineGameModeBase.cpp +++ b/Game/GigLine/Source/GigLine/GigLineGameModeBase.cpp @@ -1,5 +1,6 @@ #include "GigLineGameModeBase.h" +#include "GameFramework/PlayerController.h" #include "GigLineCharacter.h" #include "GigLineHUD.h" @@ -9,3 +10,12 @@ AGigLineGameModeBase::AGigLineGameModeBase() // ODbL attribution must be visible in the running demo, not only in docs. HUDClass = AGigLineHUD::StaticClass(); } + +void AGigLineGameModeBase::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) +{ + Super::HandleStartingNewPlayer_Implementation(NewPlayer); + UE_LOG(LogTemp, Log, TEXT("GigLine GM: player %s connected; pawn=%s input=%s"), + NewPlayer ? *NewPlayer->GetName() : TEXT("null"), + NewPlayer && NewPlayer->GetPawn() ? *NewPlayer->GetPawn()->GetName() : TEXT("NONE"), + NewPlayer && NewPlayer->PlayerInput ? TEXT("yes") : TEXT("no")); +} diff --git a/Game/GigLine/Source/GigLine/GigLineGameModeBase.h b/Game/GigLine/Source/GigLine/GigLineGameModeBase.h index 23c7f31..598b210 100644 --- a/Game/GigLine/Source/GigLine/GigLineGameModeBase.h +++ b/Game/GigLine/Source/GigLine/GigLineGameModeBase.h @@ -18,4 +18,8 @@ class GIGLINE_API AGigLineGameModeBase : public AGameModeBase public: AGigLineGameModeBase(); + +protected: + /** Logs whether a local player connected to a pawn with input (QA). */ + virtual void HandleStartingNewPlayer_Implementation(class APlayerController* NewPlayer) override; }; diff --git a/Tools/ue_create_input_assets.py b/Tools/ue_create_input_assets.py index 11180ae..ff1a9d5 100644 --- a/Tools/ue_create_input_assets.py +++ b/Tools/ue_create_input_assets.py @@ -85,7 +85,11 @@ def build(): # component and D/A carry strafe, combined by the character. A 2D value would # need per-key axis modifiers, and modifiers do not survive the save path here. ia_move = fresh("IA_Move", unreal.InputAction, unreal.InputAction_Factory) - ia_move.set_editor_property("value_type", unreal.InputActionValueType.AXIS1D) + # Boolean (pressed = 1), not AXIS1D: the C++ handler reads Get() and + # moves at full walk speed. An axis action from a keyboard key can deliver 0 + # depending on the trigger, which showed up as "character stands, W does not + # move" in QA. WASD are four separate per-direction actions below. + ia_move.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) save(ia_move) ia_look = fresh("IA_Look", unreal.InputAction, unreal.InputAction_Factory) @@ -107,15 +111,15 @@ def build(): # Two actions feed the one-dimensional move: IA_Move for forward/back, and a # second action for strafe, both reporting on the value's single component. ia_strafe = fresh("IA_Strafe", unreal.InputAction, unreal.InputAction_Factory) - ia_strafe.set_editor_property("value_type", unreal.InputActionValueType.AXIS1D) + ia_strafe.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) save(ia_strafe) ia_back = fresh("IA_MoveBack", unreal.InputAction, unreal.InputAction_Factory) - ia_back.set_editor_property("value_type", unreal.InputActionValueType.AXIS1D) + ia_back.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) save(ia_back) ia_left = fresh("IA_StrafeLeft", unreal.InputAction, unreal.InputAction_Factory) - ia_left.set_editor_property("value_type", unreal.InputActionValueType.AXIS1D) + ia_left.set_editor_property("value_type", unreal.InputActionValueType.BOOLEAN) save(ia_left) # Reset and pause are boolean: one press, no accumulator.