/// <summary> /// Initializes a contact. /// </summary> /// <param name="body1">The first body.</param> /// <param name="body2">The second body.</param> /// <param name="point1">The collision point in worldspace</param> /// <param name="point2">The collision point in worldspace</param> /// <param name="n">The normal pointing to body2.</param> /// <param name="penetration">The estimated penetration depth.</param> public void Initialize(RigidBody body1, RigidBody body2, ref FPVector point1, ref FPVector point2, ref FPVector n, FP penetration, bool newContact, ContactSettings settings) { this.body1 = body1; this.body2 = body2; this.normal = n; normal.Normalize(); this.p1 = point1; this.p2 = point2; this.newContact = newContact; FPVector.Subtract(ref p1, ref body1.position, out relativePos1); FPVector.Subtract(ref p2, ref body2.position, out relativePos2); FPVector.Transform(ref relativePos1, ref body1.invOrientation, out realRelPos1); FPVector.Transform(ref relativePos2, ref body2.invOrientation, out realRelPos2); this.initialPen = penetration; this.penetration = penetration; body1IsMassPoint = body1.isParticle; body2IsMassPoint = body2.isParticle; // Material Properties if (newContact) { treatBody1AsStatic = body1.isStatic; treatBody2AsStatic = body2.isStatic; accumulatedNormalImpulse = FP.Zero; accumulatedTangentImpulse = FP.Zero; lostSpeculativeBounce = FP.Zero; switch (settings.MaterialCoefficientMixing) { case ContactSettings.MaterialCoefficientMixingType.TakeMaximum: staticFriction = FPMath.Max(body1.staticFriction, body2.staticFriction); dynamicFriction = FPMath.Max(body1.staticFriction, body2.staticFriction); restitution = FPMath.Max(body1.restitution, body2.restitution); break; case ContactSettings.MaterialCoefficientMixingType.TakeMinimum: staticFriction = FPMath.Min(body1.staticFriction, body2.staticFriction); dynamicFriction = FPMath.Min(body1.staticFriction, body2.staticFriction); restitution = FPMath.Min(body1.restitution, body2.restitution); break; case ContactSettings.MaterialCoefficientMixingType.UseAverage: staticFriction = (body1.staticFriction + body2.staticFriction) * FP.Half; dynamicFriction = (body1.staticFriction + body2.staticFriction) * FP.Half; restitution = (body1.restitution + body2.restitution) * FP.Half; break; } } this.settings = settings; }
/// <summary> /// Passes a axis aligned bounding box to the shape where collision /// could occour. /// </summary> /// <param name="box">The bounding box where collision could occur.</param> /// <returns>The upper index with which <see cref="SetCurrentShape"/> can be /// called.</returns> public override int Prepare(ref TSBBox box) { // simple idea: the terrain is a grid. x and z is the position in the grid. // y the height. we know compute the min and max grid-points. All quads // between these points have to be checked. // including overflow exception prevention if (box.min.x < boundings.min.x) { minX = 0; } else { minX = (int)FP.Floor(((box.min.x - sphericalExpansion) / scaleX)); minX = (int)FPMath.Max(minX, 0); } if (box.max.x > boundings.max.x) { maxX = heightsLength0 - 1; } else { maxX = (int)FP.Ceiling(((box.max.x + sphericalExpansion) / scaleX)); maxX = (int)FPMath.Min(maxX, heightsLength0 - 1); } if (box.min.z < boundings.min.z) { minZ = 0; } else { minZ = (int)FP.Floor(((box.min.z - sphericalExpansion) / scaleZ)); minZ = (int)FPMath.Max(minZ, 0); } if (box.max.z > boundings.max.z) { maxZ = heightsLength1 - 1; } else { maxZ = (int)FP.Ceiling((FP)((box.max.z + sphericalExpansion) / scaleZ)); maxZ = (int)FPMath.Min(maxZ, heightsLength1 - 1); } numX = maxX - minX; numZ = maxZ - minZ; // since every quad contains two triangles we multiply by 2. return(numX * numZ * 2); }
/// <summary> /// Iteratively solve this constraint. /// </summary> public override void Iterate() { if (skipConstraint) { return; } FP jv = FPVector.Dot(ref body1.linearVelocity, ref jacobian[0]); jv += FPVector.Dot(ref body2.linearVelocity, ref jacobian[1]); FP softnessScalar = accumulatedImpulse * softnessOverDt; FP lambda = -effectiveMass * (jv + bias + softnessScalar); if (behavior == DistanceBehavior.LimitMinimumDistance) { FP previousAccumulatedImpulse = accumulatedImpulse; accumulatedImpulse = FPMath.Max(accumulatedImpulse + lambda, 0); lambda = accumulatedImpulse - previousAccumulatedImpulse; } else if (behavior == DistanceBehavior.LimitMaximumDistance) { FP previousAccumulatedImpulse = accumulatedImpulse; accumulatedImpulse = FPMath.Min(accumulatedImpulse + lambda, 0); lambda = accumulatedImpulse - previousAccumulatedImpulse; } else { accumulatedImpulse += lambda; } FPVector temp; if (!body1.isStatic) { FPVector.Multiply(ref jacobian[0], lambda * body1.inverseMass, out temp); FPVector.Add(ref temp, ref body1.linearVelocity, out body1.linearVelocity); } if (!body2.isStatic) { FPVector.Multiply(ref jacobian[1], lambda * body2.inverseMass, out temp); FPVector.Add(ref temp, ref body2.linearVelocity, out body2.linearVelocity); } }
/// <summary> /// Iteratively solve this constraint. /// </summary> public override void Iterate() { if (skipConstraint) { return; } FP jv = body1.linearVelocity * jacobian[0] + body1.angularVelocity * jacobian[1] + body2.linearVelocity * jacobian[2] + body2.angularVelocity * jacobian[3]; FP softnessScalar = accumulatedImpulse * softnessOverDt; FP lambda = -effectiveMass * (jv + bias + softnessScalar); if (behavior == DistanceBehavior.LimitMinimumDistance) { FP previousAccumulatedImpulse = accumulatedImpulse; accumulatedImpulse = FPMath.Max(accumulatedImpulse + lambda, 0); lambda = accumulatedImpulse - previousAccumulatedImpulse; } else if (behavior == DistanceBehavior.LimitMaximumDistance) { FP previousAccumulatedImpulse = accumulatedImpulse; accumulatedImpulse = FPMath.Min(accumulatedImpulse + lambda, 0); lambda = accumulatedImpulse - previousAccumulatedImpulse; } else { accumulatedImpulse += lambda; } if (!body1.isStatic) { body1.linearVelocity += body1.inverseMass * lambda * jacobian[0]; body1.angularVelocity += FPVector.Transform(lambda * jacobian[1], body1.invInertiaWorld); } if (!body2.isStatic) { body2.linearVelocity += body2.inverseMass * lambda * jacobian[2]; body2.angularVelocity += FPVector.Transform(lambda * jacobian[3], body2.invInertiaWorld); } }
private Dictionary <Body, FPVector2> ApplyImpulse(FPVector2 pos, FP radius, FP force, FP maxForce, HashSet <Body> overlappingBodies) { Dictionary <Body, FPVector2> forces = new Dictionary <Body, FPVector2>(overlappingBodies.Count); foreach (Body overlappingBody in overlappingBodies) { if (IsActiveOn(overlappingBody)) { FP distance = FPVector2.Distance(pos, overlappingBody.Position); FP forcePercent = GetPercent(distance, radius); FPVector2 forceVector = pos - overlappingBody.Position; forceVector *= 1f / FP.Sqrt(forceVector.x * forceVector.x + forceVector.y * forceVector.y); forceVector *= FPMath.Min(force * forcePercent, maxForce); forceVector *= -1; overlappingBody.ApplyLinearImpulse(forceVector); forces.Add(overlappingBody, forceVector); } } return(forces); }
/// <summary> /// Activate the explosion at the specified position. /// </summary> /// <param name="pos">The position where the explosion happens </param> /// <param name="radius">The explosion radius </param> /// <param name="maxForce">The explosion force at the explosion point (then is inversely proportional to the square of the distance)</param> /// <returns>A list of bodies and the amount of force that was applied to them.</returns> public Dictionary <Fixture, FPVector2> Activate(FPVector2 pos, FP radius, FP maxForce) { AABB aabb; aabb.LowerBound = pos + new FPVector2(-radius, -radius); aabb.UpperBound = pos + new FPVector2(radius, radius); Fixture[] shapes = new Fixture[MaxShapes]; // More than 5 shapes in an explosion could be possible, but still strange. Fixture[] containedShapes = new Fixture[5]; bool exit = false; int shapeCount = 0; int containedShapeCount = 0; // Query the world for overlapping shapes. World.QueryAABB( fixture => { if (fixture.TestPoint(ref pos)) { if (IgnoreWhenInsideShape) { exit = true; return(false); } containedShapes[containedShapeCount++] = fixture; } else { shapes[shapeCount++] = fixture; } // Continue the query. return(true); }, ref aabb); if (exit) { return(new Dictionary <Fixture, FPVector2>()); } Dictionary <Fixture, FPVector2> exploded = new Dictionary <Fixture, FPVector2>(shapeCount + containedShapeCount); // Per shape max/min angles for now. FP[] vals = new FP[shapeCount * 2]; int valIndex = 0; for (int i = 0; i < shapeCount; ++i) { PolygonShape ps; CircleShape cs = shapes[i].Shape as CircleShape; if (cs != null) { // We create a "diamond" approximation of the circle Vertices v = new Vertices(); FPVector2 vec = FPVector2.zero + new FPVector2(cs.Radius, 0); v.Add(vec); vec = FPVector2.zero + new FPVector2(0, cs.Radius); v.Add(vec); vec = FPVector2.zero + new FPVector2(-cs.Radius, cs.Radius); v.Add(vec); vec = FPVector2.zero + new FPVector2(0, -cs.Radius); v.Add(vec); ps = new PolygonShape(v, 0); } else { ps = shapes[i].Shape as PolygonShape; } if ((shapes[i].Body.BodyType == BodyType.Dynamic) && ps != null) { FPVector2 toCentroid = shapes[i].Body.GetWorldPoint(ps.MassData.Centroid) - pos; FP angleToCentroid = FP.Atan2(toCentroid.y, toCentroid.x); FP min = FP.MaxValue; FP max = FP.MinValue; FP minAbsolute = 0.0f; FP maxAbsolute = 0.0f; for (int j = 0; j < ps.Vertices.Count; ++j) { FPVector2 toVertex = (shapes[i].Body.GetWorldPoint(ps.Vertices[j]) - pos); FP newAngle = FP.Atan2(toVertex.y, toVertex.x); FP diff = (newAngle - angleToCentroid); diff = (diff - FP.Pi) % (2 * FP.Pi); // the minus pi is important. It means cutoff for going other direction is at 180 deg where it needs to be if (diff < 0.0f) { diff += 2 * FP.Pi; // correction for not handling negs } diff -= FP.Pi; if (FP.Abs(diff) > FP.Pi) { continue; // Something's wrong, point not in shape but exists angle diff > 180 } if (diff > max) { max = diff; maxAbsolute = newAngle; } if (diff < min) { min = diff; minAbsolute = newAngle; } } vals[valIndex] = minAbsolute; ++valIndex; vals[valIndex] = maxAbsolute; ++valIndex; } } Array.Sort(vals, 0, valIndex, _rdc); _data.Clear(); bool rayMissed = true; for (int i = 0; i < valIndex; ++i) { Fixture fixture = null; FP midpt; int iplus = (i == valIndex - 1 ? 0 : i + 1); if (vals[i] == vals[iplus]) { continue; } if (i == valIndex - 1) { // the single edgecase midpt = (vals[0] + FP.PiTimes2 + vals[i]); } else { midpt = (vals[i + 1] + vals[i]); } midpt = midpt / 2; FPVector2 p1 = pos; FPVector2 p2 = radius * new FPVector2(FP.Cos(midpt), FP.Sin(midpt)) + pos; // RaycastOne bool hitClosest = false; World.RayCast((f, p, n, fr) => { Body body = f.Body; if (!IsActiveOn(body)) { return(0); } hitClosest = true; fixture = f; return(fr); }, p1, p2); //draws radius points if ((hitClosest) && (fixture.Body.BodyType == BodyType.Dynamic)) { if ((_data.Any()) && (_data.Last().Body == fixture.Body) && (!rayMissed)) { int laPos = _data.Count - 1; ShapeData la = _data[laPos]; la.Max = vals[iplus]; _data[laPos] = la; } else { // make new ShapeData d; d.Body = fixture.Body; d.Min = vals[i]; d.Max = vals[iplus]; _data.Add(d); } if ((_data.Count > 1) && (i == valIndex - 1) && (_data.Last().Body == _data.First().Body) && (_data.Last().Max == _data.First().Min)) { ShapeData fi = _data[0]; fi.Min = _data.Last().Min; _data.RemoveAt(_data.Count - 1); _data[0] = fi; while (_data.First().Min >= _data.First().Max) { fi.Min -= FP.PiTimes2; _data[0] = fi; } } int lastPos = _data.Count - 1; ShapeData last = _data[lastPos]; while ((_data.Count > 0) && (_data.Last().Min >= _data.Last().Max)) // just making sure min<max { last.Min = _data.Last().Min - FP.PiTimes2; _data[lastPos] = last; } rayMissed = false; } else { rayMissed = true; // raycast did not find a shape } } for (int i = 0; i < _data.Count; ++i) { if (!IsActiveOn(_data[i].Body)) { continue; } FP arclen = _data[i].Max - _data[i].Min; FP first = FPMath.Min(MaxEdgeOffset, EdgeRatio * arclen); int insertedRays = FP.Ceiling((((arclen - 2.0f * first) - (MinRays - 1) * MaxAngle) / MaxAngle)).AsInt(); if (insertedRays < 0) { insertedRays = 0; } FP offset = (arclen - first * 2.0f) / ((FP)MinRays + insertedRays - 1); //Note: This loop can go into infinite as it operates on FPs. //Added FPEquals with a large epsilon. for (FP j = _data[i].Min + first; j < _data[i].Max || MathUtils.FPEquals(j, _data[i].Max, 0.0001f); j += offset) { FPVector2 p1 = pos; FPVector2 p2 = pos + radius * new FPVector2(FP.Cos(j), FP.Sin(j)); FPVector2 hitpoint = FPVector2.zero; FP minlambda = FP.MaxValue; List <Fixture> fl = _data[i].Body.FixtureList; for (int x = 0; x < fl.Count; x++) { Fixture f = fl[x]; RayCastInput ri; ri.Point1 = p1; ri.Point2 = p2; ri.MaxFraction = 50f; RayCastOutput ro; if (f.RayCast(out ro, ref ri, 0)) { if (minlambda > ro.Fraction) { minlambda = ro.Fraction; hitpoint = ro.Fraction * p2 + (1 - ro.Fraction) * p1; } } // the force that is to be applied for this particular ray. // offset is angular coverage. lambda*length of segment is distance. FP impulse = (arclen / (MinRays + insertedRays)) * maxForce * 180.0f / FP.Pi * (1.0f - KBEngine.FPMath.Min(FP.One, minlambda)); // We Apply the impulse!!! FPVector2 vectImp = FPVector2.Dot(impulse * new FPVector2(FP.Cos(j), FP.Sin(j)), -ro.Normal) * new FPVector2(FP.Cos(j), FP.Sin(j)); _data[i].Body.ApplyLinearImpulse(ref vectImp, ref hitpoint); // We gather the fixtures for returning them if (exploded.ContainsKey(f)) { exploded[f] += vectImp; } else { exploded.Add(f, vectImp); } if (minlambda > 1.0f) { hitpoint = p2; } } } } // We check contained shapes for (int i = 0; i < containedShapeCount; ++i) { Fixture fix = containedShapes[i]; if (!IsActiveOn(fix.Body)) { continue; } FP impulse = MinRays * maxForce * 180.0f / FP.Pi; FPVector2 hitPoint; CircleShape circShape = fix.Shape as CircleShape; if (circShape != null) { hitPoint = fix.Body.GetWorldPoint(circShape.Position); } else { PolygonShape shape = fix.Shape as PolygonShape; hitPoint = fix.Body.GetWorldPoint(shape.MassData.Centroid); } FPVector2 vectImp = impulse * (hitPoint - pos); fix.Body.ApplyLinearImpulse(ref vectImp, ref hitPoint); if (!exploded.ContainsKey(fix)) { exploded.Add(fix, vectImp); } } return(exploded); }
public void ApplyForces(MoveInfo move) { if (freeze) { return; } controlScript.normalizedJumpArc = (Fix64)1 - ((verticalForce + verticalTotalForce) / (verticalTotalForce * 2)); Fix64 appliedFriction = (moveDirection != 0 || controlScript.myInfo.physics.highMovingFriction) ? UFE.config.selectedStage._groundFriction : controlScript.myInfo.physics._friction; if (move != null && move.ignoreFriction) { appliedFriction = 0; } if (controlScript.activePullIn != null) { worldTransform.position = FPVector.Lerp(worldTransform.position, controlScript.activePullIn.position, UFE.fixedDeltaTime * controlScript.activePullIn.speed); if (controlScript.activePullIn.forceStand && !IsGrounded()) { ForceGrounded(); } if (FPVector.Distance(controlScript.activePullIn.position, worldTransform.position) <= controlScript.activePullIn._targetDistance || controlScript.currentSubState != SubStates.Stunned) { controlScript.activePullIn = null; } } else { if (!IsGrounded()) { appliedFriction = 0; if (verticalForce == 0) { verticalForce = -.1; } } if (horizontalForce != 0 && !isTakingOff) { if (horizontalForce > 0) { horizontalForce -= appliedFriction * UFE.fixedDeltaTime; horizontalForce = FPMath.Max(0, horizontalForce); } else if (horizontalForce < 0) { horizontalForce += appliedFriction * UFE.fixedDeltaTime; horizontalForce = FPMath.Min(0, horizontalForce); } Fix64 leftCameraBounds = opWorldTransform.position.x - (UFE.config.cameraOptions._maxDistance / 2); Fix64 rightCameraBounds = opWorldTransform.position.x + (UFE.config.cameraOptions._maxDistance / 2); bool bouncingOnCamera = false; if (controlScript.currentHit != null && controlScript.currentHit.bounceOnCameraEdge && (worldTransform.position.x <= leftCameraBounds || worldTransform.position.x >= rightCameraBounds)) { bouncingOnCamera = true; } if (wallBounceTimes < UFE.config.wallBounceOptions._maximumBounces && controlScript.currentSubState == SubStates.Stunned && controlScript.currentState != PossibleStates.Down && UFE.config.wallBounceOptions.bounceForce != Sizes.None && FPMath.Abs(horizontalForce) >= UFE.config.wallBounceOptions._minimumBounceForce && (worldTransform.position.x <= UFE.config.selectedStage._leftBoundary || worldTransform.position.x >= UFE.config.selectedStage._rightBoundary || bouncingOnCamera) && controlScript.currentHit != null && controlScript.currentHit.wallBounce && !isWallBouncing) { if (controlScript.currentHit.overrideForcesOnWallBounce) { if (controlScript.currentHit.resetWallBounceHorizontalPush) { horizontalForce = 0; } if (controlScript.currentHit.resetWallBounceVerticalPush) { verticalForce = 0; } Fix64 addedH = -controlScript.currentHit._wallBouncePushForce.x; Fix64 addedV = controlScript.currentHit._wallBouncePushForce.y; AddForce(new FPVector(addedH, addedV, 0), controlScript.mirror); } else { if (UFE.config.wallBounceOptions.bounceForce == Sizes.Small) { horizontalForce /= -1.4; } else if (UFE.config.wallBounceOptions.bounceForce == Sizes.Medium) { horizontalForce /= -1.2; } else if (UFE.config.wallBounceOptions.bounceForce == Sizes.High) { horizontalForce *= -1; } } wallBounceTimes++; if (verticalForce > 0 || !IsGrounded()) { if (moveSetScript.basicMoves.airWallBounce.animMap[0].clip != null) { controlScript.currentHitAnimation = moveSetScript.basicMoves.airWallBounce.name; } } else { if (controlScript.currentHit.knockOutOnWallBounce) { moveSetScript.PlayBasicMove(moveSetScript.basicMoves.standingWallBounceKnockdown); controlScript.currentHitAnimation = moveSetScript.basicMoves.standingWallBounceKnockdown.name; } else { moveSetScript.PlayBasicMove(moveSetScript.basicMoves.standingWallBounce); controlScript.currentHitAnimation = moveSetScript.basicMoves.standingWallBounce.name; } } if (UFE.config.wallBounceOptions.bouncePrefab != null) { GameObject pTemp = UFE.SpawnGameObject(UFE.config.wallBounceOptions.bouncePrefab, transform.position, Quaternion.identity, Mathf.RoundToInt(UFE.config.wallBounceOptions.bounceKillTime * UFE.config.fps)); pTemp.transform.rotation = UFE.config.wallBounceOptions.bouncePrefab.transform.rotation; if (UFE.config.wallBounceOptions.sticky) { pTemp.transform.parent = transform; } //pTemp.transform.localPosition = Vector3.zero; } if (UFE.config.wallBounceOptions.shakeCamOnBounce) { controlScript.shakeCameraDensity = UFE.config.wallBounceOptions._shakeDensity; } UFE.PlaySound(UFE.config.wallBounceOptions.bounceSound); isWallBouncing = true; } worldTransform.Translate((horizontalForce * UFE.fixedDeltaTime), 0, 0); } if (move == null || (move != null && !move.ignoreGravity)) { if ((verticalForce < 0 && !IsGrounded()) || verticalForce > 0) { verticalForce -= appliedGravity * UFE.fixedDeltaTime; worldTransform.Translate((moveDirection * UFE.fixedDeltaTime) * controlScript.myInfo.physics._jumpDistance, (verticalForce * UFE.fixedDeltaTime), 0); } else if (verticalForce < 0 && IsGrounded() && controlScript.currentSubState != SubStates.Stunned) { verticalForce = 0; } } } Fix64 minDist = opWorldTransform.position.x - UFE.config.cameraOptions._maxDistance; Fix64 maxDist = opWorldTransform.position.x + UFE.config.cameraOptions._maxDistance; worldTransform.position = new FPVector(FPMath.Clamp(worldTransform.position.x, minDist, maxDist), worldTransform.position.y, worldTransform.position.z); worldTransform.position = new FPVector( FPMath.Clamp(worldTransform.position.x, UFE.config.selectedStage._leftBoundary, UFE.config.selectedStage._rightBoundary), FPMath.Max(worldTransform.position.y, UFE.config.selectedStage._groundHeight), worldTransform.position.z); if (controlScript.currentState == PossibleStates.Down) { return; } if (IsGrounded() && controlScript.currentState != PossibleStates.Down) { if (verticalTotalForce != 0) { if (groundBounceTimes < UFE.config.groundBounceOptions._maximumBounces && controlScript.currentSubState == SubStates.Stunned && UFE.config.groundBounceOptions.bounceForce != Sizes.None && verticalForce <= -UFE.config.groundBounceOptions._minimumBounceForce && controlScript.currentHit.groundBounce) { if (controlScript.currentHit.overrideForcesOnGroundBounce) { if (controlScript.currentHit.resetGroundBounceHorizontalPush) { horizontalForce = 0; } if (controlScript.currentHit.resetGroundBounceVerticalPush) { verticalForce = 0; } Fix64 addedH = controlScript.currentHit._groundBouncePushForce.x; Fix64 addedV = controlScript.currentHit._groundBouncePushForce.y; AddForce(new FPVector(addedH, addedV, 0), controlScript.mirror); } else { if (UFE.config.groundBounceOptions.bounceForce == Sizes.Small) { AddForce(new FPVector(0, (-verticalForce / 2.4), 0), 1); } else if (UFE.config.groundBounceOptions.bounceForce == Sizes.Medium) { AddForce(new FPVector(0, (-verticalForce / 1.8), 0), 1); } else if (UFE.config.groundBounceOptions.bounceForce == Sizes.High) { AddForce(new FPVector(0, (-verticalForce / 1.2), 0), 1); } } groundBounceTimes++; if (!isGroundBouncing) { controlScript.stunTime += airTime + UFE.config.knockDownOptions.air._knockedOutTime; if (moveSetScript.basicMoves.groundBounce.animMap[0].clip != null) { controlScript.currentHitAnimation = moveSetScript.basicMoves.groundBounce.name; moveSetScript.PlayBasicMove(moveSetScript.basicMoves.groundBounce); } if (UFE.config.groundBounceOptions.bouncePrefab != null) { GameObject pTemp = UFE.SpawnGameObject(UFE.config.groundBounceOptions.bouncePrefab, transform.position, Quaternion.identity, Mathf.RoundToInt(UFE.config.groundBounceOptions.bounceKillTime * UFE.config.fps)); pTemp.transform.rotation = UFE.config.groundBounceOptions.bouncePrefab.transform.rotation; if (UFE.config.groundBounceOptions.sticky) { pTemp.transform.parent = transform; } //pTemp.transform.localPosition = Vector3.zero; } if (UFE.config.groundBounceOptions.shakeCamOnBounce) { controlScript.shakeCameraDensity = UFE.config.groundBounceOptions._shakeDensity; } UFE.PlaySound(UFE.config.groundBounceOptions.bounceSound); isGroundBouncing = true; } return; } verticalTotalForce = 0; airTime = 0; moveSetScript.totalAirMoves = 0; currentAirJumps = 0; BasicMoveInfo airAnimation = null; string downAnimation = ""; isGroundBouncing = false; groundBounceTimes = 0; Fix64 animationSpeed = 0; Fix64 delayTime = 0; if (controlScript.currentMove != null && controlScript.currentMove.hitAnimationOverride) { return; } if (controlScript.currentSubState == SubStates.Stunned) { if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.airRecovery.name)) { controlScript.stunTime = 0; controlScript.currentState = PossibleStates.Stand; } else { controlScript.stunTime = UFE.config.knockDownOptions.air._knockedOutTime + UFE.config.knockDownOptions.air._standUpTime; // Hit Clips if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.getHitKnockBack.name) && moveSetScript.basicMoves.getHitKnockBack.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.getHitKnockBack; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.getHitHighKnockdown.name) && moveSetScript.basicMoves.getHitHighKnockdown.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.getHitHighKnockdown; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); controlScript.stunTime = UFE.config.knockDownOptions.high._knockedOutTime + UFE.config.knockDownOptions.high._standUpTime; } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.getHitMidKnockdown.name) && moveSetScript.basicMoves.getHitMidKnockdown.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.getHitMidKnockdown; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); controlScript.stunTime = UFE.config.knockDownOptions.highLow._knockedOutTime + UFE.config.knockDownOptions.highLow._standUpTime; } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.getHitSweep.name) && moveSetScript.basicMoves.getHitSweep.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.getHitSweep; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); controlScript.stunTime = UFE.config.knockDownOptions.sweep._knockedOutTime + UFE.config.knockDownOptions.sweep._standUpTime; } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.getHitCrumple.name) && moveSetScript.basicMoves.getHitCrumple.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.getHitCrumple; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); // Stage Clips } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.standingWallBounceKnockdown.name) && moveSetScript.basicMoves.standingWallBounceKnockdown.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.standingWallBounceKnockdown; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); controlScript.stunTime = UFE.config.knockDownOptions.wallbounce._knockedOutTime + UFE.config.knockDownOptions.wallbounce._standUpTime; } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.airWallBounce.name) && moveSetScript.basicMoves.airWallBounce.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.airWallBounce; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); controlScript.stunTime = UFE.config.knockDownOptions.wallbounce._knockedOutTime + UFE.config.knockDownOptions.wallbounce._standUpTime; // Fall Clips } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.fallingFromAirHit.name) && moveSetScript.basicMoves.fallingFromAirHit.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.fallingFromAirHit; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); } else if (moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.fallingFromGroundBounce.name) && moveSetScript.basicMoves.fallingFromGroundBounce.animMap[1].clip != null) { airAnimation = moveSetScript.basicMoves.fallingFromGroundBounce; downAnimation = moveSetScript.GetAnimationString(airAnimation, 2); } else { if (moveSetScript.basicMoves.fallDown.animMap[0].clip == null) { Debug.LogError("Fall Down From Air Hit animation not found! Make sure you have it set on Character -> Basic Moves -> Fall Down From Air Hit"); } airAnimation = moveSetScript.basicMoves.fallDown; downAnimation = moveSetScript.GetAnimationString(airAnimation, 1); } controlScript.currentState = PossibleStates.Down; } } else if (controlScript.currentState != PossibleStates.Stand) { if (moveSetScript.basicMoves.landing.animMap[0].clip != null && (controlScript.currentMove == null || (controlScript.currentMove != null && controlScript.currentMove.cancelMoveWheLanding))) { controlScript.isAirRecovering = false; airAnimation = moveSetScript.basicMoves.landing; moveDirection = 0; isLanding = true; controlScript.KillCurrentMove(); delayTime = (Fix64)controlScript.myInfo.physics.landingDelay / (Fix64)UFE.config.fps; UFE.DelaySynchronizedAction(ResetLanding, delayTime); if (airAnimation.autoSpeed) { animationSpeed = moveSetScript.GetAnimationLength(airAnimation.name) / delayTime; } } if (controlScript.currentState != PossibleStates.Crouch) { controlScript.currentState = PossibleStates.Stand; } } if (airAnimation != null) { if (downAnimation != "") { moveSetScript.PlayBasicMove(airAnimation, downAnimation); } else { moveSetScript.PlayBasicMove(airAnimation); } if (animationSpeed != 0) { moveSetScript.SetAnimationSpeed(airAnimation.name, animationSpeed); } } } if (controlScript.currentSubState != SubStates.Stunned && !controlScript.isBlocking && !controlScript.blockStunned && move == null && !isTakingOff && !isLanding && controlScript.currentState == PossibleStates.Stand) { if (moveDirection > 0 && controlScript.mirror == -1 || moveDirection < 0 && controlScript.mirror == 1) { if (moveSetScript.basicMoves.moveForward.animMap[0].clip == null) { Debug.LogError("Move Forward animation not found! Make sure you have it set on Character -> Basic Moves -> Move Forward"); } if (!moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.moveForward.name)) { moveSetScript.PlayBasicMove(moveSetScript.basicMoves.moveForward); } } else if (moveDirection > 0 && controlScript.mirror == 1 || moveDirection < 0 && controlScript.mirror == -1) { if (moveSetScript.basicMoves.moveBack.animMap[0].clip == null) { Debug.LogError("Move Back animation not found! Make sure you have it set on Character -> Basic Moves -> Move Back"); } if (!moveSetScript.IsAnimationPlaying(moveSetScript.basicMoves.moveBack.name)) { moveSetScript.PlayBasicMove(moveSetScript.basicMoves.moveBack); } } } } else if (verticalForce > 0 || !IsGrounded()) { if (move != null && controlScript.currentState == PossibleStates.Stand) { controlScript.currentState = PossibleStates.NeutralJump; } if (move == null && verticalForce / verticalTotalForce > 0 && verticalForce / verticalTotalForce <= 1) { if (isGroundBouncing) { return; } if (moveDirection == 0) { controlScript.currentState = PossibleStates.NeutralJump; } else { if (moveDirection > 0 && controlScript.mirror == -1 || moveDirection < 0 && controlScript.mirror == 1) { controlScript.currentState = PossibleStates.ForwardJump; } if (moveDirection > 0 && controlScript.mirror == 1 || moveDirection < 0 && controlScript.mirror == -1) { controlScript.currentState = PossibleStates.BackJump; } } BasicMoveInfo airAnimation = moveSetScript.basicMoves.jumpStraight; if (controlScript.currentSubState == SubStates.Stunned) { if (isWallBouncing && moveSetScript.basicMoves.airWallBounce.animMap[0].clip != null) { airAnimation = moveSetScript.basicMoves.airWallBounce; } else if (moveSetScript.basicMoves.getHitKnockBack.animMap[0].clip != null && FPMath.Abs(horizontalForce) > UFE.config.comboOptions._knockBackMinForce && UFE.config.comboOptions._knockBackMinForce > 0) { airAnimation = moveSetScript.basicMoves.getHitKnockBack; airTime *= (Fix64)2; } else { if (moveSetScript.basicMoves.getHitAir.animMap[0].clip == null) { Debug.LogError("Get Hit Air animation not found! Make sure you have it set on Character -> Basic Moves -> Get Hit Air"); } airAnimation = moveSetScript.basicMoves.getHitAir; } if (overrideStunAnimation != null) { airAnimation = overrideStunAnimation; } } else if (controlScript.isAirRecovering && (moveSetScript.basicMoves.airRecovery.animMap[0].clip != null)) { airAnimation = moveSetScript.basicMoves.airRecovery; } else { if (moveSetScript.basicMoves.jumpForward.animMap[0].clip != null && controlScript.currentState == PossibleStates.ForwardJump) { airAnimation = moveSetScript.basicMoves.jumpForward; } else if (moveSetScript.basicMoves.jumpBack.animMap[0].clip != null && controlScript.currentState == PossibleStates.BackJump) { airAnimation = moveSetScript.basicMoves.jumpBack; } else { if (moveSetScript.basicMoves.jumpStraight.animMap[0].clip == null) { Debug.LogError("Jump animation not found! Make sure you have it set on Character -> Basic Moves -> Jump Straight"); } airAnimation = moveSetScript.basicMoves.jumpStraight; } } if (!overrideAirAnimation && !moveSetScript.IsAnimationPlaying(airAnimation.name)) { moveSetScript.PlayBasicMove(airAnimation); if (airAnimation.autoSpeed) { moveSetScript.SetAnimationNormalizedSpeed(airAnimation.name, (moveSetScript.GetAnimationLength(airAnimation.name) / airTime)); } } } else if (move == null && verticalForce / verticalTotalForce <= 0) { BasicMoveInfo airAnimation = moveSetScript.basicMoves.fallStraight; if (isGroundBouncing && moveSetScript.basicMoves.fallingFromGroundBounce.animMap[0].clip != null) { airAnimation = moveSetScript.basicMoves.fallingFromGroundBounce; } else if (isWallBouncing && moveSetScript.basicMoves.airWallBounce.animMap[0].clip != null) { airAnimation = moveSetScript.basicMoves.airWallBounce; } else { if (controlScript.currentSubState == SubStates.Stunned) { if (moveSetScript.basicMoves.getHitKnockBack.animMap[0].clip != null && FPMath.Abs(horizontalForce) > UFE.config.comboOptions._knockBackMinForce && UFE.config.comboOptions._knockBackMinForce > 0) { airAnimation = moveSetScript.basicMoves.getHitKnockBack; } else { airAnimation = moveSetScript.basicMoves.getHitAir; if (moveSetScript.basicMoves.fallingFromAirHit.animMap[0].clip != null) { airAnimation = moveSetScript.basicMoves.fallingFromAirHit; } else if (moveSetScript.basicMoves.getHitAir.animMap[0].clip == null) { Debug.LogError("Air Juggle animation not found! Make sure you have it set on Character -> Basic Moves -> Air Juggle"); } } if (overrideStunAnimation != null) { airAnimation = overrideStunAnimation; } } else if (controlScript.isAirRecovering && (moveSetScript.basicMoves.airRecovery.animMap[0].clip != null)) { airAnimation = moveSetScript.basicMoves.airRecovery; } else { if (moveSetScript.basicMoves.fallForward.animMap[0].clip != null && controlScript.currentState == PossibleStates.ForwardJump) { airAnimation = moveSetScript.basicMoves.fallForward; } else if (moveSetScript.basicMoves.fallBack.animMap[0].clip != null && controlScript.currentState == PossibleStates.BackJump) { airAnimation = moveSetScript.basicMoves.fallBack; } else { if (moveSetScript.basicMoves.fallStraight.animMap[0].clip == null) { Debug.LogError("Fall animation not found! Make sure you have it set on Character -> Basic Moves -> Fall Straight"); } airAnimation = moveSetScript.basicMoves.fallStraight; } } } if (!overrideAirAnimation && !moveSetScript.IsAnimationPlaying(airAnimation.name)) { moveSetScript.PlayBasicMove(airAnimation); if (airAnimation.autoSpeed) { moveSetScript.SetAnimationNormalizedSpeed(airAnimation.name, (moveSetScript.GetAnimationLength(airAnimation.name) / airTime)); } } } } if (horizontalForce == 0 && verticalForce == 0) { moveDirection = 0; } }