/// <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 (Ladder2D 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 (RaycastCollider2D feetCollider in feetColliders) {
				RaycastHit2D hitFeet = new RaycastHit2D ();
				RaycastHit2D hitLadder = new RaycastHit2D ();
				RaycastHit2D hitGround = new RaycastHit2D ();
				float closest = float.MaxValue;
				float closestLadder = float.MaxValue;
				
				RaycastHit2D[] 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 (RaycastHit2D collision in feetCollisions) {
					// If we got a ladder also keep reference to ground
					if (collision.collider != null && collision.collider.gameObject.layer == climableLayer) {
						if (collision.fraction * (feetCollider.distance + slopes.slopeLookAhead) < closestLadder) {
							hitLadder = collision;	
							closestLadder = collision.fraction * (feetCollider.distance + slopes.slopeLookAhead);
						}
					} else if (collision.fraction * (feetCollider.distance + slopes.slopeLookAhead) < closest) {
						hitFeet = collision;
						closest = collision.fraction * (feetCollider.distance + slopes.slopeLookAhead);
					}
					if (collision.collider.gameObject.layer != climableLayer)
						groundedFeet++;
				}
				
				// If ladder is closest collider
				if (hitLadder.collider != null && hitFeet.collider != null && hitLadder.fraction * (feetCollider.distance + slopes.slopeLookAhead) < closest) {
					// Only assign ground if its a true hit, not a slope look ahead hit
					if (hitFeet.fraction * (feetCollider.distance + slopes.slopeLookAhead) <= 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.fraction * (feetCollider.distance + slopes.slopeLookAhead)))).y;	
				// Standing on a something that has an action when you stand on it
				if (hitFeet.collider != null) {
					Platform2D platform = hitFeet.collider.gameObject.GetComponent<Platform2D> ();
					if (platform != null && feetCollider.distance >= hitFeet.fraction * (feetCollider.distance + slopes.slopeLookAhead)) {
						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 TopStepPlatform2D) {
							hasHitFeet = true;
							maxForce = force;
							hitGameObject = hitLadder.collider.gameObject;
						}
					}
					// Climbing 
					if (climbing.allowClimbing && hitLadder.collider != null && hitLadder.fraction * (feetCollider.distance + slopes.slopeLookAhead) <= 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 TopStepPlatform2D) && (startedClimbing || (climbing.autoStick && hitGround.collider == null && (dismounting == null || (myParent != null && dismounting != ((Ladder2D)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.fraction * (feetCollider.distance + slopes.slopeLookAhead))).y;		
											if (maxForce < groundForce)
												maxForce = groundForce;
											isClimbingUpOrDown = false;
											startedClimbing = false;
										}
										
									}
								}
							}
						}
					} else {
						// Calculate slope
						if (slopes.allowSlopes && hitFeet.collider.gameObject.layer != climableLayer) {
							if (lastHitDistance < 0.0f) {
								lastHitDistance = hitFeet.fraction * (feetCollider.distance + slopes.slopeLookAhead);
								lastHitX = feetCollider.offset.x;
								if (slopeCount == -1)
									slopeCount = 0;
							} else {
								slope += Mathf.Atan ((lastHitDistance - hitFeet.fraction * (feetCollider.distance + slopes.slopeLookAhead)) / (feetCollider.offset.x - lastHitX)) * Mathf.Rad2Deg;
								slopeCount++;
								lastHitDistance = hitFeet.fraction * (feetCollider.distance + slopes.slopeLookAhead);
								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.fraction * (feetCollider.distance + slopes.slopeLookAhead) <= 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 LadderCollider2D && ((LadderCollider2D)myParent).control.ShouldPlayLedgeClimb (this))) {
						if (characterInput.y < 0.0f && myParent is TopStepPlatform2D && 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 TopStepPlatform2D)) {
							isLadderTopClimbing = true; 
							ladderTopClimbState = LadderTopState.CLIMBING_UP;
							// Force position to be exactly right
							myTransform.position = new Vector3 (myTransform.position.x, ((LadderCollider2D)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);	
					}
				}
				if (velocity.y < 0.0f) 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 (RaycastCollider2D 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 Ladder2D) {
			Dismount ((Ladder2D)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);
			}
		}
		
		// Hitting Head
		if (velocity.y > 0.0f || isClimbing || (myParent != null && myParent.velocity.y > 0.0f)) {
			float maxForce = 0.0f;
			foreach (RaycastCollider2D headCollider in headColliders) {
				RaycastHit2D hitHead = headCollider.GetCollision (1 << backgroundLayer);
				float force = (hitHead.normal * (headCollider.distance - (hitHead.fraction * headCollider.distance ))).y;
				if (hitHead.collider != null) {
					// Action on headbut
					Platform2D platform = hitHead.collider.gameObject.GetComponent<Platform2D> ();
					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 = 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;
					}
				}
			}
			// "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;	
				}
			}
		}
	}
Exemplo n.º 3
0
    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);
            int             climbableLayer    = 0;
            LadderControl   control           = null;
            LadderControl2D control2D         = null;

            // Create parent
            GameObject ladderGo = new GameObject();
            ladderGo.name = "NewLadder";

            if (controller != null)
            {
                climbableLayer = controller.climableLayer;
            }
            else if (controller2D != null)
            {
                climbableLayer = controller2D.climableLayer;
            }

            if (useLadderTop)
            {
                if (controller != null)
                {
                    control = ladderGo.AddComponent <LadderControl> ();
                    control.ledgeClimbOffset = ladderTopDistance;
                    // Create Top Step
                    GameObject topStepGo = new GameObject();
                    topStepGo.name  = "TopStep";
                    topStepGo.layer = climbableLayer;
                    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 if (controller2D != null)
                {
                    control2D = ladderGo.AddComponent <LadderControl2D> ();
                    control2D.ledgeClimbOffset = ladderTopDistance;
                    // Create Top Step
                    GameObject topStepGo = new GameObject();
                    topStepGo.name  = "TopStep";
                    topStepGo.layer = climbableLayer;
                    TopStepPlatform2D topStep2D         = topStepGo.AddComponent <TopStepPlatform2D>();
                    BoxCollider2D     topStepCollider2D = topStepGo.AddComponent <BoxCollider2D>();
                    topStep2D.control                 = control2D;
                    topStepCollider2D.size            = new Vector3(ladderWidth / 2.0f, stepSize / 2.0f, 0.5f);
                    topStep2D.transform.parent        = ladderGo.transform;
                    topStep2D.transform.localPosition = new Vector3(0, -1 * (stepSize / 2.0f), 0);
                }
            }
            else
            {
                if (controller != null)
                {
                    control = ladderGo.AddComponent <LadderControl> ();
                }
                if (controller2D != null)
                {
                    control2D = ladderGo.AddComponent <LadderControl2D> ();
                }
                if (control != null)
                {
                    control.disableLedgeClimb = true;
                }
                if (control2D != null)
                {
                    control2D.disableLedgeClimb = true;
                }
            }

            // Create Steps

            int count = (int)((totalLength + (useLadderTop ? ladderTopDistance : 0)) / stepDistance);
            while (count > 0)
            {
                GameObject stepGo = new GameObject();
                if (controller != null)
                {
                    stepGo.name  = "Step" + count;
                    stepGo.layer = climbableLayer;
                    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);
                }
                else if (controller2D != null)
                {
                    stepGo.name  = "Step" + count;
                    stepGo.layer = climbableLayer;
                    LadderCollider2D step2D         = stepGo.AddComponent <LadderCollider2D>();
                    BoxCollider2D    stepCollider2D = stepGo.AddComponent <BoxCollider2D>();
                    step2D.control                 = control2D;
                    stepCollider2D.size            = new Vector3(ladderWidth / 2.0f, stepSize / 2.0f, 0.5f);
                    step2D.transform.parent        = ladderGo.transform;
                    step2D.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);
        }
    }