using System.Runtime.InteropServices; using Prism; namespace FPSExample { public class FPSPlayer : Entity { public float WalkingSpeed = 10.0f; public float RunSpeed = 20.0f; public float JumpForce = 50.0f; public float m_Radius = 0.5f; public float TorqueStrength = 10.0f; [NonSerialized] public float MouseSensitivity = 10.0f; public float CameraForwardOffset = 0.5f; public float CameraYOffset = 0.5f; private bool m_Colliding = false; private float m_CurrentSpeed; private RigidBodyComponent m_RigidBody; private TransformComponent m_Transform; private TransformComponent m_CameraTransform; private Entity m_CameraEntity; private Vec2 m_LastMousePosition; private float m_CurrentYMovement = 0.0f; private Vec2 m_MovementDirection = new Vec2(0.0f); private bool m_ShouldJump = false; void OnCreate() { m_Transform = GetComponent(); m_RigidBody = GetComponent(); m_CurrentSpeed = WalkingSpeed; AddCollisionBeginCallback((n) => { m_Colliding = true; }); AddCollisionEndCallback((n) => { m_Colliding = false; }); m_CameraEntity = FindEntityByTag("Camera"); m_CameraTransform = m_CameraEntity.GetComponent(); m_LastMousePosition = Input.GetMousePosition(); Input.SetCursorMode(Input.CursorMode.Locked); int size = Marshal.SizeOf(); Console.WriteLine($"C# size of Transform: {size}"); } void OnUpdate(float ts) { if (Input.IsKeyPressed(KeyCode.Escape) && Input.GetCursorMode() == Input.CursorMode.Locked) Input.SetCursorMode(Input.CursorMode.Normal); if(Input.IsMouseButtonPressed(Input.MouseButton.Left) && Input.GetCursorMode() == Input.CursorMode.Normal) Input.SetCursorMode(Input.CursorMode.Locked); m_CurrentSpeed = Input.IsKeyPressed(KeyCode.LeftControl) ? RunSpeed : WalkingSpeed; UpdateRayCasting(); UpdateMovementInput(); UpdateRotation(ts); UpdateCameraTransform(); } void OnPhysicsUpdate(float fixedTimeStep) { UpdateMovement(); } private void UpdateRotation(float ts) { Vec2 currentMousePosition = Input.GetMousePosition(); Vec2 delta = m_LastMousePosition - currentMousePosition; m_CurrentYMovement = delta.X * MouseSensitivity * ts; float xRotation = delta.Y * MouseSensitivity * ts; // m_RigidBody.Rotate(new Vec3(0.0f, m_CurrentYMovement, 0.0f)); if (delta.X != 0 || delta.Y != 0) { m_CameraTransform.Rotation += new Vec3(xRotation, m_CurrentYMovement, 0.0f); } m_CameraTransform.Rotation = new Vec3(Mathf.Clamp(m_CameraTransform.Rotation.X, -89.0f, 89.0f), m_CameraTransform.Rotation.YZ); m_LastMousePosition = currentMousePosition; } private void UpdateMovementInput() { if (Input.IsKeyPressed(KeyCode.W)){ m_MovementDirection.Y -= 1.0f; Debug.Log("KeyPressed: W"); } else if (Input.IsKeyPressed(KeyCode.S)){ m_MovementDirection.Y += 1.0f; Debug.Log("KeyPressed: S"); } else m_MovementDirection.Y = 0.0f; if(Input.IsKeyPressed(KeyCode.A)){ m_MovementDirection.X += 1.0f; Debug.Log("KeyPressed: A"); } else if (Input.IsKeyPressed(KeyCode.D)){ m_MovementDirection.X -= 1.0f; Debug.Log("KeyPressed: D"); } else m_MovementDirection.X = 0.0f; m_ShouldJump = Input.IsKeyPressed(KeyCode.Space) && !m_ShouldJump; } Collider[] colliders = new Collider[10]; private void UpdateRayCasting() { RaycastHit hitInfo; if (Input.IsKeyPressed(KeyCode.H) && Physics.Raycast(m_CameraTransform.Translation + (m_CameraTransform.Transform.Forward), m_CameraTransform.Transform.Forward, 20.0f, out hitInfo)) { FindEntityByID(hitInfo.EntityID).GetComponent().Mesh.GetMaterial(0).Set("u_AlbedoColor", new Vec3(1.0f ,0.0f, 0.0f)); } if (Input.IsKeyPressed(KeyCode.I)) { // NOTE: The NonAlloc version of Overlap functions should be used when possible since it doesn't allocate a new array // whenever you call it. The normal versions allocates a brand new array every time. int numColliders = Physics.OverlapBoxNonAlloc(m_Transform.Translation, new Vec3(1.0f), colliders); Console.WriteLine("Colliders: {0}", numColliders); // When using NonAlloc it's not safe to use a foreach loop since some of the colliders may not exist for (int i = 0; i < numColliders; i++) { Console.WriteLine(colliders[i]); } } } private void UpdateMovement() { // 2. 计算期望的移动方向(世界坐标系) Vec3 desiredDirection = Vec3.Zero; if (m_MovementDirection.LengthSquared() != 0.0f) { Vec3 right = m_CameraTransform.Transform.Right; Vec3 forward = m_CameraTransform.Transform.Forward; right.Y = 0; forward.Y = 0; right.Normalize(); forward.Normalize(); desiredDirection = right * m_MovementDirection.X + forward * m_MovementDirection.Y; desiredDirection.Normalize(); /* Vec3 movement = right * m_MovementDirection.X + forward * m_MovementDirection.Y; movement.Normalize(); Vec3 velocity = movement * m_CurrentSpeed; velocity.Y = m_RigidBody.GetLinearVelocity().Y; m_RigidBody.SetLinearVelocity(velocity); */ } Vec3 targetAngularVelocity = Vec3.Zero; if (desiredDirection.LengthSquared() > 0.01f) { Vec3 up = Vec3.Up; Vec3 rotationAxis = Vec3.Cross(desiredDirection, up).Normalized(); float angularSpeed = 2 * (m_CurrentSpeed / m_Radius); targetAngularVelocity = rotationAxis * angularSpeed; Vec3 currentAngular = m_RigidBody.GetAngularVelocity(); Vec3 angularDiff = targetAngularVelocity - currentAngular; float inertia = 0.4f * m_RigidBody.Mass * m_Radius * m_Radius; Vec3 torque = angularDiff * inertia * TorqueStrength; m_RigidBody.AddTorque(torque, RigidBodyComponent.ForceMode.Force); } /* // 3. 获取当前角速度,计算需要调整的差值 Vec3 currentAngular = m_RigidBody.GetAngularVelocity(); Vec3 angularDiff = targetAngularVelocity - currentAngular; // 4. 施加扭矩:扭矩 = 转动惯量 * 角加速度 // 球体的转动惯量 I = (2/5) * m * r^2 (实心球) float inertia = TorqueStrength * m_RigidBody.Mass * m_Radius * m_Radius; // 设定一个系数控制响应速度(可调) float torqueStrength = 5.0f; Vec3 torque = angularDiff * inertia * torqueStrength; m_RigidBody.AddTorque(-torque, RigidBodyComponent.ForceMode.Force); */ if (m_ShouldJump && m_Colliding) { Debug.Log("Jump"); m_RigidBody.AddForce(Vec3.Up * JumpForce, RigidBodyComponent.ForceMode.Impulse); m_ShouldJump = false; } } private void UpdateCameraTransform(){ Vec3 position = m_Transform.Translation + m_CameraTransform.Transform.Forward * CameraForwardOffset; position.Y += CameraYOffset; m_CameraTransform.Translation = position; } } }