// // PlayerController.cpp:FPS 玩家控制脚本——挂在场景 primary 相机实体 // (CameraComponent.primary=true)上,直接驱动该实体的 Transform。 // // 为什么驱动实体而不是"编辑器相机":PIE 内嵌播放/游戏形态的渲染源 // 是 primary 相机实体(SceneRenderer::BuildFrame 分派,main.cpp 亦 // 注明"PIE 内嵌播放:渲染源已切 primary 相机,编辑器相机停驱")。 // 实体位姿一动,视口所见即玩家所见——这才是 Unity/UE 的正道; // EditorCamera 是视口 UI 状态,不是场景实体,脚本够不着也不该够。 // // 能力(全部走 Input 门面,零引擎改动): // - 鼠标捕捉:OnStart 捕捉(FPS 语义:光标隐入、无限转向); // Esc 释放(交还编辑器交互);未捕捉时点视口重新捕捉 // - 视角:GetMouseDelta 增量 → yaw 绕世界 up 左乘 + pitch 绕本地 // X 右乘(±89° 钳制,与编辑器相机 Camera::Update 同语义) // - 移动:WASD 水平 + Space 升 / Ctrl 降(飞行式,无重力—— // 打靶场微调机位用);Shift 加速 // // 注意:捕捉态下编辑器 UI 收不到鼠标——这是游戏体验的代价; // Esc 是唯一退路(释放后可点 Inspector/改参数)。 // // 配套:TestShoot(挂同一实体)射击,同样只在捕捉态开火。 // #include #include #include #include using namespace Engine; // Input 门面 using Engine::Scripting::ParamBuilder; namespace { class PlayerController final : public Engine::Scripting::ScriptBehavior { public: const char* Name() const override { return "PlayerController"; } void DeclareParams(ParamBuilder& pb) override { pb.Field("LookSensitivity", &m_Sensitivity, 0.12f); // 度/像素 pb.Field("MoveSpeed", &m_MoveSpeed, 4.0f); // 平移速度 m/s pb.Field("RunSpeed", &m_RunSpeed, 8.0f); // Shift 加速 m/s pb.Field("CaptureOnStart", &m_CaptureOnStart, true); } void OnStart() override { if (m_CaptureOnStart) { Input::SetCursorCaptured(true); } } void OnUpdate(f32 dt) override { // ---- 捕捉管理:Esc 释放 / 点视口重捕 ---- bool captured = Input::IsCursorCaptured(); if (captured && Input::IsKeyDown(Input::Key::Escape)) { Input::SetCursorCaptured(false); captured = false; } const bool leftDown = Input::IsMouseButtonDown(Input::MouseButton::Left); if (!captured) { // 未捕捉时点击(点在视口内才收得到——ImGui 捕获的 // 区域静默)→ 重捕进入操作态 if (leftDown && !m_LeftWasDown) { Input::SetCursorCaptured(true); } m_LeftWasDown = leftDown; return; // 释放态:不转向不移动(编辑器交互让路) } m_LeftWasDown = leftDown; Look(); Move(dt); } private: void Look() { f32 dx = 0.0f, dy = 0.0f; Input::GetMouseDelta(dx, dy); if (dx == 0.0f && dy == 0.0f) return; auto& tr = Transform(); const glm::quat cur = tr.GetRotationQuat(); // yaw:绕世界 up 左乘(不随俯仰改变旋转轴) const glm::quat yaw = glm::angleAxis(glm::radians(-dx * m_Sensitivity), glm::vec3(0.0f, 1.0f, 0.0f)); // pitch:绕本地 X 右乘,钳制 ±89°(防过顶翻转) const glm::vec3 fwd = cur * glm::vec3(0.0f, 0.0f, -1.0f); const f32 pitch = glm::asin(glm::clamp(fwd.y, -1.0f, 1.0f)); const f32 kPitchLimit = glm::radians(89.0f); const f32 pitchTarget = glm::clamp(pitch + glm::radians(-dy * m_Sensitivity), -kPitchLimit, kPitchLimit); const glm::quat pitchStep = glm::angleAxis(pitchTarget - pitch, glm::vec3(1.0f, 0.0f, 0.0f)); tr.SetRotationQuat(yaw * (cur * pitchStep)); } void Move(f32 dt) { glm::vec3 axis{0.0f}; if (Input::IsKeyDown(Input::Key::W)) axis.z += 1.0f; if (Input::IsKeyDown(Input::Key::S)) axis.z -= 1.0f; if (Input::IsKeyDown(Input::Key::D)) axis.x += 1.0f; if (Input::IsKeyDown(Input::Key::A)) axis.x -= 1.0f; if (Input::IsKeyDown(Input::Key::Space)) axis.y += 1.0f; if (Input::IsKeyDown(Input::Key::LeftControl)) axis.y -= 1.0f; if (glm::dot(axis, axis) < 1e-6f) return; const glm::quat r = Transform().GetRotationQuat(); const glm::vec3 fwd = r * glm::vec3(0.0f, 0.0f, -1.0f); const glm::vec3 right = r * glm::vec3(1.0f, 0.0f, 0.0f); // 水平走视线投影(含俯仰 = 飞行感),升降用世界 Y glm::vec3 move = fwd * axis.z + right * axis.x + glm::vec3(0.0f, axis.y, 0.0f); const f32 len = glm::length(move); if (len < 1e-6f) return; move /= len; const f32 speed = Input::IsKeyDown(Input::Key::LeftShift) ? m_RunSpeed : m_MoveSpeed; auto& tr = Transform(); tr.SetPosition(tr.GetWorldPosition() + move * speed * dt); } f32 m_Sensitivity = 0.12f; f32 m_MoveSpeed = 4.0f; f32 m_RunSpeed = 8.0f; bool m_CaptureOnStart = true; bool m_LeftWasDown = false; // 重捕用的左键上升沿基准 }; ENGINE_REGISTER_SCRIPT(PlayerController) } // namespace