}//Update /// <summary> /// Cast Horizontal ray with respect to "rayLength" sign. SKINWIDTH is added /// to the rayLength here. /// </summary> /// <param name="rayLength">Length of the ray and the direction (based of /// its sign) to cast. </param> /// <param name="positionIndex">Position of the ray relative to Number of /// rays count set in Props.</param> /// <returns>Casted ray meta data.</returns> public RaycastMeta CastHorizontalRay(float rayLength, int positionIndex) { float directionX = Mathf.Sign(rayLength); rayLength = Mathf.Abs(rayLength) + SKINWIDTH; Vector2 rayOrigin = (directionX == -1) ? rayOrigins.bottomLeft : rayOrigins.bottomRight; Vector2 direction = Vector2.right * directionX; rayOrigin += Vector2.up * (raySpacing.x * positionIndex); RaycastHit2D hit = Physics2D.Raycast(rayOrigin, direction, rayLength, this.Props.CollisionMask); RaycastMeta meta = new RaycastMeta(rayOrigin, direction, hit, rayLength, positionIndex); #if UNITY_EDITOR if (this.DebugProps.On) { if (positionIndex == 0) Debug.DrawRay(meta.Origin, meta.FullDir, DebugProps.First); else Debug.DrawRay(meta.Origin, meta.FullDir, DebugProps.NoHit); if (hit) Debug.DrawRay(meta.Origin, meta.FullDir, DebugProps.Hit); } #endif return meta; }//Raycast
}//Update public virtual void MovePassangers() { if (_cd == null) return; Vector3 deltaMovement = this.Velocity; float direction = 1f; deltaMovement.y = direction * 0.3f; _cd.UpdateRayOrigin(); _cd.HorizontalCollisions(ref deltaMovement); _cd.VerticalCollisions(ref deltaMovement); for (int i = 0; i < _cd.VerticalRayMeta.Length; i++) { RaycastMeta meta = _cd.VerticalRayMeta[i]; if(!meta.Ray) continue; float moveX = this.Velocity.x; float moveY = this.Velocity.y; if (Mathf.Sign(this.Velocity.y) > 0) moveY = Velocity.y - (meta.Ray.distance - 0.055f) * direction; //var mvmnt = meta.Ray.collider.GetComponent<MovementControls>(); //if (mvmnt == null) // continue; //var player = meta.Ray.collider.GetComponent<Player>(); //player.IsOnMovingPlatform = true; //meta.Ray.collider.transform.Translate(new Vector3(moveX, moveY)); //mvmnt.SetVelocity(new Vector2(moveX, moveY)); }//for }//MovePassangers
}//Raycast /// <summary> /// Cast Vertically (down/up) ray with respect to "rayLength" sign. SKINWIDTH is added /// to the rayLength here. /// </summary> /// <param name="rayLength">Length of the ray and the direction (based of /// its sign) to cast. </param> /// <param name="positionIndex">Position of the ray relative to Number of /// rays count set in Props.</param> /// <returns>Casted ray meta data.</returns> public RaycastMeta CastVerticalRay(float rayLength, int positionIndex) { float directionY = Mathf.Sign(rayLength); //current movement direction (up/down) rayLength = Mathf.Abs(rayLength) + SKINWIDTH; Vector2 rayOrigin = (directionY == -1) ? rayOrigins.bottomLeft : rayOrigins.topLeft; Vector2 direction = Vector2.up * directionY; rayOrigin += Vector2.right * (raySpacing.y * positionIndex); RaycastHit2D hit = Physics2D.Raycast(rayOrigin, direction, rayLength, this.Props.CollisionMask); //To ignore raycast hits on itself, unselect the "Queries Start In Colliders" //at the "Project Settings -> Physics 2D RaycastMeta meta = new RaycastMeta(rayOrigin, direction, hit, rayLength, positionIndex); #if UNITY_EDITOR if (this.DebugProps.On) { if (positionIndex == 0) Debug.DrawRay(meta.Origin, meta.FullDir, DebugProps.First); else Debug.DrawRay(meta.Origin, meta.FullDir, DebugProps.NoHit); if (hit) Debug.DrawRay(meta.Origin, meta.FullDir, DebugProps.Hit); } #endif return meta; }//Raycast
}//RaycastMeta public void Set(RaycastMeta meta) { this.Origin = meta.Origin; this.Direction = meta.Direction; this.Ray = meta.Ray; this.Length = meta.Length; this.Angle = meta.Angle; this.Index = meta.Index; }
}//HorizontalCollisions /// <summary> /// Raycast vertically with respect to deltaMovement. /// This should be called every frame. /// </summary> public void VerticalCollisions(ref Vector3 deltaMovement) { float directionY = Mathf.Sign(deltaMovement.y); //current movement direction (up/down) if (this.VerticalRayMeta == null || this.VerticalRayMeta.Length != this.Props.VerticalRays) this.VerticalRayMeta = new RaycastMeta[this.Props.VerticalRays]; int numOfHits = 0; for (int i = 0; i < this.Props.VerticalRays; i++) { RaycastMeta meta = this.CastVerticalRay(deltaMovement.y, i); //Update Global ray Meta. if(this.VerticalRayMeta[i] == null) this.VerticalRayMeta[i] = new RaycastMeta(); this.VerticalRayMeta[i].Set(meta); //if (meta.Ray && meta.Ray.collider.gameObject.GetHashCode() == this.gameObject.GetHashCode()) { // Debug.Log(meta.Ray.collider.gameObject.name); // continue; //} //---- NO HIT ---- if (!meta.Ray) { if(i == 0) IsFallingThrough = false; continue; } if (this.Props.IsIgnoreTag(meta.HitTag)) continue; bool isPlatformTag = this.Props.IsPlatformTag(meta.HitTag); numOfHits++; if (IsFallingThrough && isPlatformTag) continue; else IsFallingThrough = false; if (directionY > 0) if (!IsFallingThrough && isPlatformTag) continue; deltaMovement.y = (meta.Ray.distance - SKINWIDTH) * directionY; Below = directionY == -1; Above = directionY == 1; }//for if(numOfHits == 0) IsFallingThrough = false; }//VerticalCollisions
}//Raycast /// <summary> /// Raycast horizontally (left/right) with respect to deltaMovement. /// This should be called every frame. /// </summary> public void HorizontalCollisions(ref Vector3 deltaMovement) { float directionX = Mathf.Sign(deltaMovement.x); if (this.HorizontalRayMeta == null || this.HorizontalRayMeta.Length != this.Props.HorizontalRays) this.HorizontalRayMeta = new RaycastMeta[this.Props.HorizontalRays]; for (int i = 0; i < this.Props.HorizontalRays; i++) { RaycastMeta meta = this.CastHorizontalRay(deltaMovement.x, i); if(this.HorizontalRayMeta[i] == null) this.HorizontalRayMeta[i] = new RaycastMeta(); this.HorizontalRayMeta[i].Set(meta); //---- NO HIT ---- if (!meta.Ray) continue; //Ignore rayhit for Platforms or Ignorable tags. if(this.Props.IsIgnoreTag(meta.HitTag)) continue; if(this.Props.IsPlatformTag(meta.HitTag)) continue; float slopeAngle = Vector2.Angle(meta.Ray.normal, Vector2.up); this.HorizontalRayMeta[i].Angle = slopeAngle; if (i == 0 && slopeAngle <= this.Props.ClimbingSlope) { float distanceToSlopeStart = 0; if (slopeAngle != slopeAngleLastFrame) { distanceToSlopeStart = meta.Ray.distance - SKINWIDTH; deltaMovement.x -= distanceToSlopeStart * directionX; } ClimbSlope(ref deltaMovement, slopeAngle); deltaMovement.x += distanceToSlopeStart * directionX; }//if if (!IsOnSlope || slopeAngle > this.Props.ClimbingSlope) { deltaMovement.x = (meta.Ray.distance - SKINWIDTH) * directionX; if (IsOnSlope) deltaMovement.y = Mathf.Tan(currentSlopeAngle * Mathf.Deg2Rad) * Mathf.Abs(deltaMovement.x); Left = directionX == -1; Right = directionX == 1; }//if this.HorizontalRayMeta[i].Length = meta.Ray.distance; }//for }//HorizontalCollisions
}//SnapToLadder public bool GetActorGrounded(float distance=0) { if (this.Actor == null) return false; if (this.Actor.CollisionDetectionCmp == null) return false; CollisionDetection cd = this.Actor.CollisionDetectionCmp; bool isGrounded = cd.Below || cd.IsOnSlope; //Already grounded? No need to cast rays then... if(isGrounded) return true; if(distance != 0) { RaycastMeta rayMeta = cd.CastVerticalRay(distance, cd.Props.VerticalRays / 2); return rayMeta.Ray; } return isGrounded; }//CheckGround
}//Update public bool IsOnPlatform() { //RaycastMeta[] rays = _raycastGround.OnRaycast(); RaycastMeta rays = _cd.CastVerticalRay(-1, 2); int index = (int)(rays.Length / 2); if (rays == null || !rays.Ray) { return(false); } GameObject hittedObj = rays.Ray.transform.gameObject; if (hittedObj == null) { return(false); } return(hittedObj.tag.ToLower().Equals(PlatformTag.ToLower())); }//IsOnPlatform
}//InitComponents /// <summary> /// Cast multiple rays from center of the sphere to its radius + SkinWidth. /// TODO: some useful docs here. /// </summary> public virtual RaycastMeta[] OnRaycast() { Vector2 start = RayOrigin + Offset; //Bottom right corner float length = RayLength; this.RayHits = new RaycastMeta[NumberOfRays]; bIsOnSlope = false; for (int i = 0; i < this.NumberOfRays; i++) { Vector2 rayOrigin = start; rayOrigin -= RayDirectionPlacement * (GetRaySpacing() * i); var ray = Physics2D.Raycast(rayOrigin, RayDirection, length, CollisionLayer); //Saving Ray meta for future use outside of this for loop. //Not important for actualy circular raycasting. RaycastMeta rayMeta = new RaycastMeta { Origin = start, Direction = RayDirection, Length = ray.distance, Index = i, Ray = ray, Angle = Vector2.SignedAngle(ray.normal, Vector2.up) }; this.RayHits[i] = rayMeta; Color debugColor = DebugColors.NoHit; if (DebugColors.On) { if (i == 0) { debugColor = DebugColors.First; } if (i == NumberOfRays - 1) { debugColor = DebugColors.Last; } } //No soup for this ray. if (!ray || ray.collider.gameObject == this.gameObject) { this.RayHits[i].Angle = 0; if (DebugColors.On) { Debug.DrawRay(rayOrigin, RayDirection * length, debugColor); } continue; }//if not ray if (!bIsOnSlope && rayMeta.Angle != 0) { if (i < this.NumberOfRays / 2) { bIsOnSlope = true; } //numOfRaysOnSlope++; } if (DebugColors.On) { Debug.DrawRay(rayOrigin, RayDirection * ray.distance, DebugColors.Hit); //use red to indicate collision } if (ray.collider.gameObject != this.gameObject) { HasCollision = !bIsOnSlope; } }//for return(this.RayHits); }//CircularRaycast