/// <summary> /// Dismount the specified Ladder. This is used to stop /// autosticking to the same ladder you just dismounted. /// </summary> /// <param name="ladder">Ladder control to dismount.</param> public void Dismount(Ladder ladder) { Unparent (); dismounting = ladder.control; startedClimbing = false; }
protected void MoveInYDirection(bool grounded) { float slope = 0.0f; int slopeCount = -1; bool isClimbing = false; bool cantClimbDown = false; bool hasHitHead = false; int climbCount = 0; groundedFeet = 0; isClimbingUpOrDown = false; // Create a copy of character input which we can modify; float characterInputY = characterInput.y; // Ignore input if stunned if (stunTimer > 0.0f) characterInputY = 0.0f; // Limit Velocity if (velocity.y < (movement.terminalVelocity * (IsSwimming ? (1 - swimming.waterResistance.y) : 1))) velocity.y = movement.terminalVelocity * (IsSwimming ? (1 - swimming.waterResistance.y) : 1); if (IsSwimming && velocity.y > swimming.maxYSpeed) velocity.y = swimming.maxYSpeed; // Apply velocity if ((myParent == null || !myParent.overrideY) && (velocity.y > movement.skinSize || velocity.y * -1 > movement.skinSize)) { myTransform.Translate (0.0f, velocity.y * frameTime, 0.0f, Space.World); } if (fallThroughTimer > 0.0f) fallThroughTimer -= frameTime; // Fall/Stop bool hasHitFeet = false; if ((velocity.y <= 0.0f || startedClimbing) && ledgeDropTimer <= 0.0f) { float maxForce = 0.0f; GameObject hitGameObject = null; float lastHitDistance = -1; float lastHitX = 0.0f; foreach (RaycastCollider feetCollider in feetColliders) { RaycastHit hitFeet = new RaycastHit (); RaycastHit hitLadder = new RaycastHit (); RaycastHit hitGround = new RaycastHit (); float closest = float.MaxValue; float closestLadder = float.MaxValue; RaycastHit[] feetCollisions = feetCollider.GetAllCollision (1 << backgroundLayer | (fallThroughTimer <= 0.0f ? 1 << passThroughLayer : 0) | (climbing.allowClimbing && fallThroughTimer <= 0.0f ? 1 << climableLayer : 0), slopes.slopeLookAhead); // Get closest collision foreach (RaycastHit collision in feetCollisions) { // If we got a ladder also keep reference to ground if (collision.collider != null && collision.collider.gameObject.layer == climableLayer) { if (collision.distance < closestLadder) { hitLadder = collision; closestLadder = collision.distance; } } else if (collision.distance < closest) { hitFeet = collision; closest = collision.distance; } if (collision.collider.gameObject.layer != climableLayer) groundedFeet++; } // If ladder is closest collider if (hitLadder.collider != null && hitFeet.collider != null && hitLadder.distance < closest) { // Only assign ground if its a true hit, not a slope look ahead hit if (hitFeet.distance <= feetCollider.distance && hitFeet.collider.gameObject.layer != climableLayer) { hitGround = hitFeet; hasHitFeet = true; } hitFeet = hitLadder; } // If only hitting a ladder if (hitLadder.collider != null && hitFeet.collider == null) { hitFeet = hitLadder; } float force = (hitFeet.normal * (feetCollider.distance - hitFeet.distance)).y; // Standing on a something that has an action when you stand on it if (hitFeet.collider != null) { Platform platform = hitFeet.collider.gameObject.GetComponent<Platform> (); if (platform != null && feetCollider.distance >= hitFeet.distance) { platform.DoAction (feetCollider, this); Transform parentPlatform = platform.ParentOnStand (this); if (parentPlatform != null) { // Normal parenting (moving platforms etc) myParent = platform; if (myTransform.parent != parentPlatform) { myTransform.parent = parentPlatform; } hitGameObject = hitFeet.collider.gameObject; } // Special case for the top of a ladder if (platform is TopStepPlatform) { hasHitFeet = true; maxForce = force; hitGameObject = hitLadder.collider.gameObject; } } // Climbing if (climbing.allowClimbing && hitLadder.collider != null && hitLadder.distance <= feetCollider.distance && jumpButtonTimer <= 0.0f) { //bool ladder = hitLadder.collider.gameObject.GetComponent<Ladder>(); //if (ladder == null) hitLadder.collider.gameObject.GetComponent<Rope>(); // && ladder != null climbCount++; if (!(myParent is TopStepPlatform) && (startedClimbing || (climbing.autoStick && hitGround.collider == null && (dismounting == null || (myParent != null && dismounting != ((Ladder)myParent).control)) || characterInputY != 0))) { if (climbCount >= climbing.collidersRequired) { startedClimbing = true; isClimbing = true; hasHitFeet = true; dismounting = null; // Allow platforms to stop us climbing if (myParent == null || !myParent.overrideY) { if (characterInputY > 0.0f) { // Ensure we dont go above step maxForce = climbing.speed * frameTime; if (maxForce > force) maxForce = force; isClimbingUpOrDown = true; } else if (!cantClimbDown && characterInputY < 0.0f) { maxForce = climbing.speed * frameTime * -1; // Ensure we don't go into ground isClimbingUpOrDown = true; if (hitGround.collider != null) { float groundForce = (hitGround.normal * (feetCollider.distance - hitGround.distance)).y; if (maxForce < groundForce) maxForce = groundForce; isClimbingUpOrDown = false; startedClimbing = false; } } } } } } else { // Calculate slope if (hitFeet.collider.gameObject.layer != climableLayer) { if (lastHitDistance < 0.0f) { lastHitDistance = hitFeet.distance; lastHitX = feetCollider.offset.x; if (slopeCount == -1) slopeCount = 0; } else { slope += Mathf.Atan ((lastHitDistance - hitFeet.distance) / (feetCollider.offset.x - lastHitX)) * Mathf.Rad2Deg; slopeCount++; lastHitDistance = hitFeet.distance; lastHitX = feetCollider.offset.x; } } } // If we are hitting our feet on the ground we can't climb down a ladder if (hitLadder.collider == null && characterInputY < 0.0f && hitFeet.distance <= feetCollider.distance) { cantClimbDown = true; maxForce = 0.0f; } // Get force to apply if (force > maxForce && hitLadder.collider == null) { // We hit a blocker stop all climbing cantClimbDown = true; isClimbingUpOrDown = false; isClimbing = false; startedClimbing = false; hasHitFeet = true; maxForce = force; hitGameObject = hitFeet.collider.gameObject; } } } if (startedClimbing && climbCount < climbing.collidersRequired) { startedClimbing = false; } if (hasHitFeet) { dismounting = null; if ((myParent == null || !myParent.overrideY)) { if ((myParent is LadderCollider && ((LadderCollider)myParent).control.ShouldPlayLedgeClimb (this))) { if (characterInput.y < 0.0f && myParent is TopStepPlatform && climbCount >= climbing.collidersRequired && groundedFeet == 0) { isLadderTopClimbing = true; State = CharacterState.CLIMB_TOP_OF_LADDER_DOWN; ladderTopClimbState = LadderTopState.CLIMBING_DOWN; } else if (characterInput.y > 0.0f && !(myParent is TopStepPlatform)) { isLadderTopClimbing = true; ladderTopClimbState = LadderTopState.CLIMBING_UP; // Force position to be exactly right myTransform.position = new Vector3 (myTransform.position.x, ((LadderCollider)myParent).LedgeClimbHeight, myTransform.position.z); } else { myTransform.Translate (0.0f, maxForce, 0.0f, Space.World); } } else { myTransform.Translate (0.0f, maxForce, 0.0f, Space.World); } } velocity.y = 0.0f; if (myParent != null && hitGameObject != myParent.gameObject && !(myParent is Rope)) { Unparent (); } grounded = true; fallingTime = 0.0f; } else { if (myParent == null) { ApplyGravity (); } else if (!grounded) { ApplyGravity (); if (!isWallSliding) Unparent (); startedClimbing = false; } } } else { // If we are mving through a passthrough pltform stop gravity for a little while // As long as pass through paltforms are kept thin this works quite well. bool holdOffOnGravity = false; foreach (RaycastCollider foot in feetColliders) { if (foot.IsColliding (1 << passThroughLayer)) { holdOffOnGravity = true; } } if (!holdOffOnGravity) ApplyGravity (); } // Force dismount if we dont have enough colliders on the ladder if (climbCount < climbing.collidersRequired && myParent is Ladder) { Dismount ((Ladder)myParent); } // Stop crouch when climbing if (isClimbing) StopCrouch (); // Apply rotation from slopes if (slopes.allowSlopes) { float actualSlope = (myTransform.localEulerAngles.z % 360.0f) + (slope / (float)slopeCount); if (slopeCount > 0 && !isClimbing && (!(actualSlope > slopes.maxRotation && actualSlope < 360.0f - slopes.maxRotation))) { myTransform.Rotate (0.0f, 0.0f, slopes.rotationSpeed * (slope / (float)slopeCount)); } else if (slopeCount == -1 || isClimbing) { myTransform.localRotation = Quaternion.RotateTowards (myTransform.localRotation, Quaternion.identity, slopes.rotationSpeed * 10.0f); } if ((actualSlope > slopes.maxRotation && actualSlope < 360.0f - slopes.maxRotation)) { myTransform.localRotation = Quaternion.Euler (0, 0, 0); } } else { if (slopeCount > 0) targetSlope = (slope / (float)slopeCount); else targetSlope = 0.0f; } // Hitting Head if (velocity.y > 0.0f || isClimbing || (myParent != null && myParent.velocity.y > 0.0f)) { float maxForce = 0.0f; foreach (RaycastCollider headCollider in headColliders) { RaycastHit hitHead = headCollider.GetCollision (1 << backgroundLayer); float force = (hitHead.normal * (headCollider.distance - hitHead.distance)).y; if (hitHead.collider != null) { // Action on headbut Platform platform = hitHead.collider.gameObject.GetComponent<Platform> (); if (platform != null) { platform.DoAction (headCollider, this); } if (force < -1 * movement.skinSize && force < maxForce) { hasHitHead = true; maxForce = force; } } } if (hasHitHead) { jumpHeldTimer = jump.jumpHeldTime; myTransform.Translate (0.0f, maxForce, 0.0f, Space.World); if (velocity.y > 0.0f) velocity.y = 0.0f; } if (!isClimbing) { ApplyGravity (); } } // Jump if (!hasHitHead || isClimbing) { if (characterInput.jumpButtonDown) { if ((grounded || myParent != null) && jumpCount == 0 && jumpButtonTimer <= 0.0f) { // Special case when launching from a rope if (myParent != null && myParent is Rope) { float k = Mathf.Abs (Mathf.Cos (Mathf.Deg2Rad * myTransform.rotation.eulerAngles.z)); k = Mathf.Pow (k, ((Rope)myParent).control.jumpFlattenFactor); velocity = new Vector2 ((myParent.velocity.x + myParent.velocity.y) * ((Rope)myParent).control.ropeVelocityFactor, jump.jumpVelocity * k); jumpCount = 2; } else if (jump.inheritParentVelocityFactor != 0 && myParent != null) { velocity.y = jump.jumpVelocity + (myParent.velocity * jump.inheritParentVelocityFactor).y; velocity.x += (myParent.velocity * jump.inheritParentVelocityFactor).x; jumpCount = 1; } else { velocity.y = jump.jumpVelocity * (IsSwimming ? (1 - swimming.waterResistance.y) : 1); jumpCount = 1; } // Inherit parent velocity Unparent (); startedClimbing = false; jumpButtonTimer = jump.jumpTimer; jumpHeldTimer = 0.0f; StopCrouch (); State = CharacterState.JUMPING; } else if (jumpCount == 1 && jump.canDoubleJump && !IsSwimming) { Unparent (); startedClimbing = false; jumpCount++; velocity.y = jump.doubleJumpVelocity; StopCrouch (); State = CharacterState.DOUBLE_JUMPING; } } else if (characterInput.jumpButtonHeld && jumpHeldTimer < jump.jumpHeldTime && jumpCount == 1) { velocity.y += jump.jumpFrameVelocity * frameTime * (jump.jumpHeldTime - jumpHeldTimer); jumpHeldTimer += frameTime; } } // Swimming if (IsSwimming){ if (State != CharacterState.JUMPING && jumpButtonTimer <= 0 && characterInput.swimButtonDown) { // Note this also adds acceleration in X! velocity.y += swimming.swimStrokeAcceleration.y * frameTime; velocity.x += swimming.swimStrokeAcceleration.x * CurrentDirection * frameTime; jumpButtonTimer = swimming.swimStrokeTime; State = CharacterState.SWIMMING; } } if (jumpButtonTimer > 0.0f) jumpButtonTimer -= frameTime; if (jumpButtonTimer <= 0.0f && grounded) { jumpCount = 0; } // Reset held button timer if we release button if (!characterInput.jumpButtonHeld) { jumpHeldTimer = jump.jumpHeldTime; } // Wall jump if (wallJumpTimer > 0) { wallJumpTimer -= frameTime; } if (wall.canWallJump && (wall.wallJumpTag == "" || wall.wallJumpTag == currentWallTag)) { // Easy wall jump if (wall.easyWallJump) { if (characterInput.jumpButtonDown && isWallHolding) { Unparent(); startedClimbing = false; velocity.y = jump.jumpVelocity; jumpCount = 2; wallJumpTimer = 0.0f; jumpButtonTimer = jump.jumpTimer; jumpHeldTimer = 0.0f; StopCrouch(); State = CharacterState.WALL_JUMPING; if (wall.wallJumpOnlyInOppositeDirection) { oppositeDirectionTimer = wall.oppositeDirectionTime; if (wallJumpDirection == RC_Direction.LEFT) velocity.x = wall.wallJumpSpeed.x; else if (wallJumpDirection == RC_Direction.RIGHT) velocity.x = -1 * wall.wallJumpSpeed.x; } } } // "Hard" wall jump also works for easy wall jump if (wallJumpTimer > 0.0f) { if ((hasPressedJumpForWallJump && ((wallJumpDirection == RC_Direction.LEFT && characterInput.x > 0) || (wallJumpDirection == RC_Direction.RIGHT && characterInput.x < 0))) || (hasPressedDirectionForWallJump && characterInput.jumpButtonDown)) { Unparent(); startedClimbing = false; velocity.y = wall.wallJumpSpeed.y; jumpCount = 2; wallJumpTimer = 0.0f; jumpButtonTimer = jump.jumpTimer; jumpHeldTimer = 0.0f; StopCrouch(); State = CharacterState.WALL_JUMPING; if (wall.wallJumpOnlyInOppositeDirection) { oppositeDirectionTimer = wall.oppositeDirectionTime; if (wallJumpDirection == RC_Direction.LEFT) velocity.x = wall.wallJumpSpeed.x; else if (wallJumpDirection == RC_Direction.RIGHT) velocity.x = -1 * wall.wallJumpSpeed.x; } } else if (!hasPressedJumpForWallJump && characterInput.jumpButtonDown ) { hasPressedJumpForWallJump = true; } else if (!hasPressedDirectionForWallJump && ((wallJumpDirection == RC_Direction.LEFT && characterInput.x > 0) || (wallJumpDirection == RC_Direction.RIGHT && characterInput.x < 0))) { hasPressedDirectionForWallJump = true; } } else { hasPressedJumpForWallJump = false; hasPressedDirectionForWallJump = false; } } if (myParent != null && myParent.overrideAnimation) { State = myParent.GetAnimationState(this); } else { // Animations if ((!IsGrounded (groundedLookAhead, false) && !hasHitFeet) || jumpButtonTimer > 0.0f) { State = CharacterState.AIRBORNE; } if (velocity.y < jump.fallVelocity) { State = CharacterState.FALLING; } if (startedClimbing){ if (isClimbingUpOrDown) { State = CharacterState.CLIMBING; } else { State = CharacterState.HOLDING; } } } }
private void GenerateLadder() { try { float ladderTopDistance = float.Parse(this.ladderTopDistance); float stepSize = float.Parse(this.stepSize); float totalLength = float.Parse(this.totalLength); float stepDistance = float.Parse(this.stepDistance); float ladderWidth = float.Parse(this.ladderWidth); // Create parent GameObject ladderGo = new GameObject(); ladderGo.name = "NewLadder"; LadderControl control = ladderGo.AddComponent <LadderControl> (); if (useLadderTop) { control.ledgeClimbOffset = ladderTopDistance; // Create Top Step GameObject topStepGo = new GameObject(); topStepGo.name = "TopStep"; topStepGo.layer = controller.climableLayer; TopStepPlatform topStep = topStepGo.AddComponent <TopStepPlatform>(); BoxCollider topStepCollider = topStepGo.AddComponent <BoxCollider>(); topStep.control = control; topStepCollider.size = new Vector3(ladderWidth / 2.0f, stepSize / 2.0f, 0.5f); topStep.transform.parent = ladderGo.transform; topStep.transform.localPosition = new Vector3(0, -1 * (stepSize / 2.0f), 0); } else { control.disableLedgeClimb = true; } // Create Steps int count = (int)((totalLength + (useLadderTop ? ladderTopDistance : 0)) / stepDistance); while (count > 0) { GameObject stepGo = new GameObject(); stepGo.name = "Step" + count; stepGo.layer = controller.climableLayer; LadderCollider step = stepGo.AddComponent <LadderCollider>(); BoxCollider stepCollider = stepGo.AddComponent <BoxCollider>(); step.control = control; stepCollider.size = new Vector3(ladderWidth / 2.0f, stepSize / 2.0f, 0.5f); step.transform.parent = ladderGo.transform; step.transform.localPosition = new Vector3(0, (-1 * count * stepDistance) + (stepSize / 2.0f) + (useLadderTop ? ladderTopDistance : 0), 0); count--; } // Create Mesh if (generateMesh && meshPrefab != null) { GameObject ladderMesh = (GameObject)Instantiate(meshPrefab); ladderMesh.transform.parent = ladderGo.transform; ladderMesh.transform.localPosition = new Vector3(0, -1 * (totalLength / 2.0f), ladderMesh.transform.localPosition.z); ladderMesh.transform.localScale = new Vector3(ladderWidth, totalLength, ladderMesh.transform.localScale.z); } } catch (System.Exception ex) { Debug.LogError("Failed to generate ladder... check parameters: " + ex.Message); } }