#if false using System.Collections.Generic; using FlaxEngine; using Cabrito; using System.Diagnostics; using System.Threading.Tasks; using FlaxEngine.Assertions; namespace Game { public struct TraceInfo_NK { public RayCastHit[] hitInfos; public bool startSolid; // closest hit public float fraction; public Vector3 endPosition; public Vector3 hitNormal; public Vector3 hitPosition; // furthest hit //public float maxFraction; //public Vector3 maxHitNormal; //public Vector3 maxEndPosition; } public class PlayerMovement_NK : Script { [Limit(0, 9000), Tooltip("Base Movement speed")] public float MoveSpeed { get; set; } = 320; private float viewPitch; private float viewYaw; private float viewRoll; private InputEvent onExit = new InputEvent("Exit"); private const float collisionMargin = 0.031f * 1.666f; Actor rootActor; public override void OnAwake() { base.OnAwake(); onExit.Triggered += () => { if (Console.IsSafeToQuit) Engine.RequestExit(); }; rootActor = Actor.GetChild(0); RigidBody rigidBody = Actor.As(); rigidBody.CollisionEnter += OnCollisionEnter; rigidBody.CollisionExit += OnCollisionLeave; rigidBody.TriggerEnter += OnTriggerEnter; //rigidBody.SetSolverIterationCounts(40, 10); Console.Print("playermovement init: " + rigidBody.MaxDepenetrationVelocity.ToString()); } private void OnTriggerEnter(PhysicsColliderActor colliderActor) { Console.Print("trogger: "); } private void OnCollisionEnter(Collision collision) { //Console.Print("collision: " + collision.Contacts[0].Normal); } private void OnCollisionLeave(Collision collision) { //Console.Print("collision leave: " + collision.Contacts[0].Normal); } public override void OnDestroy() { base.OnDestroy(); onExit.Dispose(); } public override void OnStart() { var initialEulerAngles = Actor.Orientation.EulerAngles; viewPitch = initialEulerAngles.X; viewYaw = initialEulerAngles.Y; viewRoll = initialEulerAngles.Z; } /// /// Checks for overlapping colliders in place using the player's rigidbody. /// /// Position /// private Collider[] TracePlayer(Vector3 position, float tolerance = 0.0f) { Collider[] colliders = null; bool collided = false; var capsuleCollider = Actor.GetChild(); var boxCollider = Actor.GetChild(); var meshCollider = Actor.GetChild(); PhysicsColliderActor colliderActor = null; if (capsuleCollider && capsuleCollider.IsActive) { colliderActor = capsuleCollider; collided = Physics.OverlapCapsule(position, capsuleCollider.Radius + tolerance, capsuleCollider.Height + tolerance, out colliders, capsuleCollider.Orientation, uint.MaxValue, false); } else if (meshCollider && meshCollider.IsActive) { colliderActor = meshCollider; collided = Physics.OverlapConvex(position, meshCollider.CollisionData, meshCollider.Scale + tolerance, out colliders, meshCollider.Orientation, uint.MaxValue, false); } else if (boxCollider && boxCollider.IsActive) { colliderActor = boxCollider; collided = Physics.OverlapBox(position, boxCollider.OrientedBox.Extents + tolerance, out colliders, boxCollider.Orientation, uint.MaxValue, false); } else Assert.Fail("No supported colliders found for rigidbody"); if (!collided) return colliders; List collidersFiltered = new List(); foreach (var collider in colliders) { if (collider == colliderActor) continue; collidersFiltered.Add(collider); } if (collidersFiltered.Count == 0) return colliders; // self-collision? return collidersFiltered.ToArray(); } /// /// Sweeps the player rigidbody in world and returns geometry which was hit during the trace. /// /// Start position /// End position /// private TraceInfo TracePlayer(Vector3 start, Vector3 end, float tolerance = 0.0f) { TraceInfo traceInfo = new TraceInfo(); Vector3 delta = end - start; float maxDistance = delta.Length; Vector3 direction = delta.Normalized; bool collided = false; var capsuleCollider = Actor.GetChild(); var boxCollider = Actor.GetChild(); var meshCollider = Actor.GetChild(); PhysicsColliderActor colliderActor = null; if (capsuleCollider && capsuleCollider.IsActive) { colliderActor = capsuleCollider; collided = Physics.CapsuleCastAll(start, capsuleCollider.Radius + tolerance, capsuleCollider.Height, direction, out traceInfo.hitInfos, capsuleCollider.Orientation, maxDistance, uint.MaxValue, false); } else if (meshCollider && meshCollider.IsActive) { colliderActor = meshCollider; collided = Physics.ConvexCastAll(start, meshCollider.CollisionData, meshCollider.Scale + tolerance, direction, out traceInfo.hitInfos, meshCollider.Orientation, maxDistance, uint.MaxValue, false); } else if (boxCollider && boxCollider.IsActive) { colliderActor = boxCollider; collided = Physics.BoxCastAll(start, boxCollider.OrientedBox.Extents + tolerance, direction, out traceInfo.hitInfos, boxCollider.Orientation, maxDistance, uint.MaxValue, false); } if (collided) { List hitInfosFiltered = new List(); RayCastHit closest = new RayCastHit(); closest.Distance = float.MaxValue; foreach (var hitInfo in traceInfo.hitInfos) { if (hitInfo.Collider == colliderActor) continue; hitInfosFiltered.Add(hitInfo); if (hitInfo.Distance < closest.Distance && hitInfo.Distance != 0.0f) closest = hitInfo; } if (hitInfosFiltered.Count == 0) collided = false; // self-collision? else //if (closest.Distance > 0f) { if (closest.Distance == float.MaxValue) { foreach (var hitInfo in hitInfosFiltered) { if (hitInfo.Distance < closest.Distance) closest = hitInfo; } } traceInfo.hitInfos = hitInfosFiltered.ToArray(); traceInfo.fraction = closest.Distance / maxDistance; traceInfo.hitNormal = closest.Normal; traceInfo.hitPosition = closest.Point; traceInfo.endPosition = start + (delta * traceInfo.fraction); if (traceInfo.fraction == 0f && maxDistance > 0f) traceInfo.startSolid = true; } /*else { traceInfo.startSolid = true; traceInfo.fraction = 0f; }*/ } if (!collided) { traceInfo.hitInfos = new RayCastHit[0]; traceInfo.fraction = 1f; traceInfo.endPosition = end; } return traceInfo; } public override void OnDebugDraw() { base.OnDebugDraw(); var capsuleCollider = Actor.GetChild(); var boxCollider = Actor.GetChild(); var meshCollider = Actor.GetChild(); if (capsuleCollider && capsuleCollider.IsActive) { Quaternion rotation = capsuleCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f); DebugDraw.DrawWireTube(capsuleCollider.Position, rotation, capsuleCollider.Radius, capsuleCollider.Height, Color.GreenYellow * 0.8f); } else if (meshCollider && meshCollider.IsActive) { //Quaternion rotation = meshCollider.LocalOrientation * Quaternion.Euler(0f, 90f, 0f); DebugDraw.DrawWireCylinder(meshCollider.Position, meshCollider.Orientation, capsuleCollider.Radius, capsuleCollider.Height + capsuleCollider.Radius * 2, Color.GreenYellow * 0.8f); //DebugDraw.DrawWireTube(meshCollider.Position, rotation, meshCollider.Radius, meshCollider.Height, Color.GreenYellow * 0.8f); } else if (boxCollider && boxCollider.IsActive) { DebugDraw.DrawWireBox(boxCollider.OrientedBox.GetBoundingBox(), Color.GreenYellow * 0.8f); } } private void SlideMove(ref Vector3 position, bool stepUp, ref Vector3 velocity, bool asdf = false) { if (velocity.IsZero) return; Vector3 originalPosition = position; Vector3 originalVelocity = velocity; float timeleft = Time.DeltaTime; List hitNormals = new List(); for (int bump = 0; bump < 4; bump++) { Vector3 startPos = position; Vector3 endPos = position + (velocity * timeleft); TraceInfo trace = TracePlayer(startPos, endPos); // TODO: handle portals here float fraction = trace.fraction; Vector3 hitNormal = trace.hitNormal; if (trace.startSolid) { velocity = Vector3.Zero; break; } if (physicsInteractions) { RigidBody rigidBody = Actor.As(); foreach (var hit in trace.hitInfos) { if (hit.Collider.AttachedRigidBody == null || hit.Collider.AttachedRigidBody.IsKinematic) continue; Vector3 force = -hit.Normal * velocity.Length * rigidBody.Mass; //Console.Print("move force: " + (force.Length / timeleft)); hit.Collider.AttachedRigidBody.AddForce(force, ForceMode.Force); } } if (fraction > 0f) { position = trace.endPosition; hitNormals.Clear(); // this is present in some forks, not in Q3 } if (fraction >= 1f) break; timeleft *= 1.0f - fraction; // this doesn't seem to do anything, we never have any hitNormals stored here bool hitPreviousNormal = false; foreach (Vector3 normal in hitNormals) { if (Vector3.Dot(hitNormal, normal) > 0.99) { // nudge away from the same wall we hit earlier and try again velocity += hitNormal; hitPreviousNormal = true; break; } } if (hitPreviousNormal) continue; hitNormals.Add(hitNormal); if (hitNormals.Count != 1) Console.Print("hitNormals: " + hitNormals.Count); int plane; Vector3 normalMargin = Vector3.Zero; for (plane = 0; plane < hitNormals.Count; plane++) { Vector3 normal = hitNormals[plane]; // clip velocity velocity -= normal * Vector3.Dot(velocity, normal); //velocity = Vector3.ProjectOnPlane(velocity, normal); //traceOffset = normal * 1f; normalMargin += normal; //position += normal * 0.031f; int plane2; for (plane2 = 0; plane2 < hitNormals.Count; plane2++) { if (plane == plane2) continue; if (Vector3.Dot(velocity, hitNormals[plane2]) < 0f) break; } if (plane2 == hitNormals.Count) break; } // push off slightly away from the walls to not get stuck position += normalMargin.Normalized * collisionMargin; if (plane == hitNormals.Count) { if (hitNormals.Count == 2) { Vector3 dir = Vector3.Cross(hitNormals[0], hitNormals[1]); //dir.Normalize(); float dist = Vector3.Dot(dir, velocity); velocity = dist * dir; } else { velocity = Vector3.Zero; break; } } else { // nudge very slightly away from the wall to avoid getting stuck //position += trace.hitNormal * 0.01f; //velocity += trace.hitNormal * 0.01f; } // prevents bouncing against the wall if (/*velocity.Length > 0f && */Vector3.Dot(velocity, originalVelocity) <= 0f) { velocity = Vector3.Zero; break; } } } //Vector3 wishVelocity = Vector3.Zero; //Vector3 gravityVelocity = Vector3.Zero; //Vector3 currentVelocity = Vector3.Zero; private bool onGround = false; private const float friction = 4f; private const float stopspeed = 100f; private const float accelerationGround = 10f; private const float jumpVelocity = 270f; private const float maxAirSpeed = 320f; private const float maxAirStrafeSpeed = 30f; //Q2+ private const float airAcceleration = 0.4f * 0f; //Q2+ private const float airStopAcceleration = 2.5f * 0f; //Q2+ private const float airStrafeAcceleration = 70f * 0f; //CPM? private const float strafeAcceleration = 10f; //QW private const float airControl = 0f; //CPM private bool physicsInteractions = false; private bool jumped = false; private Vector3 safePosition; private Vector3 currentVelocity; public override void OnFixedUpdate() { RigidBody rigidBody = Actor.As(); Transform rootTrans = rootActor.Transform; Vector3 inputDirection = new Vector3(InputManager.GetAxis("Horizontal"), 0.0f, InputManager.GetAxis("Vertical")); Vector3 moveDirection = rootTrans.TransformDirection(inputDirection); //Vector3 position = rigidBody.Position; Vector3 velocity = rigidBody.LinearVelocity;//currentVelocity; Vector3 wishVelocity = Vector3.Zero; if (!inputDirection.IsZero) wishVelocity = moveDirection.Normalized * MoveSpeed; // categorize position onGround = true; Vector3 groundDelta = Physics.Gravity.Normalized * collisionMargin*2; Vector3 traceGroundStart = rigidBody.Position - groundDelta; Vector3 traceGroundEnd = rigidBody.Position + groundDelta; TraceInfo traceGround = TracePlayer(traceGroundStart, traceGroundEnd, -0.1f); float groundDistance = 0.1f; if (traceGround.fraction < 1f/* && !traceGround.startSolid*//*&& ddot < 0.7f*/) { /* // slope // clip velocity Vector3 bounce = -groundDelta; Vector3 bounceDir = -groundDelta.Normalized;//traceGround.hitNormal; //Vector3 velocityProjected = Vector3.ProjectOnPlane(velocity, normal); float backoff = Vector3.Dot(bounce, bounceDir) * 2f; bounce -= bounceDir * backoff; //velocity = velocityProjected; Vector3 point = (rigidBody.Position + groundDelta) + (1f - traceGround.fraction) * bounce; //Vector3 point = rigidBody.Position - ((1f - traceGround.fraction) * groundDelta); //rigidBody.Position = point; // retrace //traceGround = TracePlayer(traceGround.endPosition, point); */ } /*if (!traceGround.startSolid && traceGround.fraction < 1f && -Vector3.Dot(Physics.Gravity.Normalized, traceGround.hitNormal) < 0.7f) { // slope // clip velocity Vector3 bounce = groundDelta; //Vector3 velocityProjected = Vector3.ProjectOnPlane(velocity, normal); float backoff = Vector3.Dot(bounce, traceGround.hitNormal) * 2f; bounce -= traceGround.hitNormal * backoff; //velocity = velocityProjected; Vector3 point = (rigidBody.Position + groundDelta) + (1f - traceGround.fraction) * bounce; // retrace traceGround = TracePlayer(rigidBody.Position, rigidBody.Position + point); }*/ //if (!traceGround.startSolid && (traceGround.fraction >= 1f || // -Vector3.Dot(Physics.Gravity.Normalized, traceGround.hitNormal) < 0.7f)) float rampDot = -Vector3.Dot(Physics.Gravity.Normalized, traceGround.hitNormal); if (traceGround.fraction >= 1f) rampDot = 1f; else if (traceGround.hitNormal.IsZero) rampDot = 99f; if (traceGround.fraction >= 1f || rampDot < 0.7f) { //Console.Print("air: " + (-Vector3.Dot(Physics.Gravity.Normalized, traceGround.hitNormal)).ToString()); // falling or sliding down a slope onGround = false; } else { float closestDist = float.MaxValue; foreach (var hit in traceGround.hitInfos) { if (hit.Distance < closestDist && hit.Distance != 0f) closestDist = hit.Distance; } if (traceGround.fraction == 0f && closestDist != 0f) Console.Print("minteresting"); //Console.Print("grund: "+ (-Vector3.Dot(Physics.Gravity.Normalized, traceGround.hitNormal)).ToString()); onGround = true;//!traceGround.startSolid; //velocity.Y = 0f; // move slightly above the ground // maintain distance off the ground float dist = ((traceGroundEnd - rigidBody.Position) * (traceGround.fraction - 0.5f)).Length; if (dist < groundDistance) { var newPos = rigidBody.Position; newPos.Y += groundDistance - dist; //rigidBody.Position = newPos; //velocity.Y = 0f; } //velocity.Y = 0f; } onGround = true; //Console.Print("frac: " + traceGround.fraction + "norm: " + rampDot); //onGround |= overlaps.Length > 0; /*if (onGround && physicsInteractions && traceGround.hitInfos.Length > 0) { // apply resting force to rigid bodies under the player //bool collided = false; foreach (var hit in traceGround.hitInfos) { if (hit.Collider.AttachedRigidBody == null || hit.Collider.AttachedRigidBody.IsKinematic) continue; if (hit.Distance <= 0f) continue; //Console.Print(Physics.Gravity.Length.ToString()); Vector3 force = -hit.Normal * (Physics.Gravity.Length) * rigidBody.Mass * Time.DeltaTime; hit.Collider.AttachedRigidBody.AddForceAtPosition(force, hit.Point, ForceMode.Impulse); //collided = true; //Console.Print("downforce: " + force.Length / Time.DeltaTime); } //if (collided) // fraction = 1.0f; // finish movement and stop }*/ /*if (!onGround) Console.Print("air"); else Console.Print("ground");*/ /*if (onGround) { // snap to ground if (!traceGround.startSolid) { Vector3 newPos = rigidBody.Position; if (traceGround.fraction < 1f) { //newPos += -Physics.Gravity.Normalized * traceGround.fraction; } rigidBody.Position = newPos; } }*/ /*if (traceGround.startSolid) { Console.Print("stuk: "); rigidBody.Position = safePosition; traceGround = TracePlayer(rigidBody.Position, rigidBody.Position + Physics.Gravity.Normalized, false); //onGround = true; //currentVelocity.Y = 0f; } if (!traceGround.startSolid) { foreach (var hitInfo in traceGround.hitInfos) { var dot = Vector3.Dot(Physics.Gravity.Normalized, hitInfo.Normal); if (-dot >= 0.7) //~45deg slope { //Console.Print("d: " + hitInfo.Distance); Vector3 newPos = rigidBody.Position; if (hitInfo.Distance > 0f) newPos += Physics.Gravity.Normalized * (hitInfo.Distance - 0.01f); else newPos += hitInfo.Normal * 0.1f; rigidBody.Position = newPos; onGround = true; currentVelocity.Y = 0f; break; //if (currentVelocity.Length > 0.01f) // Console.Print("groundvel: " + currentVelocity.ToString()); //currentVelocity.Y = 0.0f; } } }*/ // jump if (onGround) { if (!jumped && InputManager.GetAction("Jump")) { jumped = true; velocity += Vector3.Up * jumpVelocity; onGround = false; } } if (jumped && !InputManager.GetAction("Jump")) // jump released jumped = false; // ground friction if (onGround) { float currentSpeed = velocity.Length; float control = currentSpeed < stopspeed ? stopspeed : currentSpeed; var drop = control * friction * Time.DeltaTime; float newspeed = currentSpeed - drop; if (newspeed < 0) newspeed = 0; if (currentSpeed < 0.0001f) velocity *= 0; else velocity *= newspeed / currentSpeed; } //bool stepUp = false; if (onGround) // ground acceleration { ApplyAcceleration(ref velocity, wishVelocity.Normalized, wishVelocity.Length, float.MaxValue, accelerationGround); } else // air acceleration { var wishspeed = wishVelocity.Length; if (wishspeed > maxAirSpeed) wishspeed = maxAirSpeed; if (strafeAcceleration != 0f) ApplyAcceleration(ref velocity, wishVelocity.Normalized, wishspeed, maxAirStrafeSpeed, strafeAcceleration); //stepUp = true; } if (!onGround) { velocity += Physics.Gravity * Time.DeltaTime; //Console.Print("grav"); } //else // Console.Print("Yv: " + currentVelocity.Y); //SlideMove(ref position, false, ref velocity); safePosition = rigidBody.Position; if (rigidBody.EnableSimulation) { //rigidBody.Position = position; //Vector3 force = velocity - rigidBody.LinearVelocity; rigidBody.LinearVelocity = velocity; //rigidBody.AddForce(force, ForceMode.Impulse); } else { //rigidBody.Position = position; rigidBody.LinearVelocity = velocity; } //rigidBody.LinearVelocity = velocity; //if (currentVelocity.Length > 0.01f) // Console.Print("vel: " + currentVelocity.ToString()); } void ApplyAcceleration(ref Vector3 velocity, Vector3 wishDir, float wishspeed, float maxWishspeed, float acceleration) { float wishspeedOrig = wishspeed; if (wishspeed > maxWishspeed) wishspeed = maxWishspeed; float currentSpeed = Vector3.Dot(velocity, wishDir); float addSpeed = wishspeed - currentSpeed; if (addSpeed <= 0f) return; float accelSpeed = acceleration * wishspeedOrig * Time.DeltaTime; if (accelSpeed > addSpeed) accelSpeed = addSpeed; velocity += accelSpeed * wishDir; } public override void OnUpdate() { float xAxis = InputManager.GetAxisRaw("Mouse X"); float yAxis = InputManager.GetAxisRaw("Mouse Y"); if (xAxis != 0.0f || yAxis != 0.0f) { var camera = rootActor.GetChild(); viewPitch += yAxis; viewYaw += xAxis; viewPitch = Mathf.Clamp(viewPitch, -90.0f, 90.0f); // root orientation must be set first rootActor.Orientation = Quaternion.Euler(0, viewYaw, 0); camera.Orientation = Quaternion.Euler(viewPitch, viewYaw, viewRoll); } } } } #endif