Tower Trove
2 Week long game project in 2D using Unity, with students at FutureGames.
As a Game Programmer I worked on the player movement, collision, controls & shooting direction.
Project Source Code.
Calculate Velocity
private void CalculateVelocity()
{
float TargetVelocityX = DirectionalInput.x * MoveSpeed;
Velocity.x = Mathf.SmoothDamp(Velocity.x, TargetVelocityX, ref VelocityXSmoothing,
(Controller.Collisions.Below) ? AccelerationTimeGrounded : AccelerationTimeAirborne);
bool Landing = animator.GetBool("IsFalling") ? true : false;
bool CeilingCollision = (animator.GetBool("IsJumping") && Controller.Collisions.Above) ? true : false;
if (CeilingCollision)
{
if (!Controller.Collisions.Below)
{
animator.SetBool("IsFalling", true);
Velocity.y += Gravity * Time.deltaTime;
if (Velocity.y < MaxGravity)
{
Velocity.y = MaxGravity;
}
}
}
}
Collision detection
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
public class Controller2D : RayCastController
{
public struct CollisionInfo
{
public bool Above, Below;
public bool Left, Right;
public bool FallingThroughPlatform;
public bool ClimbingSlope;
public bool DescendingSlope;
public bool SlidingDownMaxSlope;
public float SlopeAngle, SlopeAngleOld;
public int FaceDirection;
public Vector2 Dir;
public Vector2 DeltaMoveOld;
public Vector2 SlopeNormal;
public void Reset()
{
Above = Below = false;
Left = Right = false;
ClimbingSlope = false;
DescendingSlope = false;
SlidingDownMaxSlope = false;
SlopeNormal = Vector2.zero;
SlopeAngleOld = SlopeAngle;
SlopeAngle = 0;
}
}
[HideInInspector]
[Header("Angle for going up/down slopes")]
public float MaxSlopeAngle = 50;
public CollisionInfo Collisions;
Vector2 PlayerInput;
[Header("Click on the right circle and add main character")]
[Tooltip("This is for fliping the character, for now.")]
public SpriteRenderer PlayerSprite;
private void ResetFallingThroughPlatform()
{
Collisions.FallingThroughPlatform = false;
}
public override void Start()
{
base.Start();
Collisions.FaceDirection = 1;
}
public void Move(Vector2 DeltaMove, Vector2 _PlayerInput, bool StandingOnPlatform = false)
{
UpdateRayCastOrigins();
Collisions.Reset();
Collisions.DeltaMoveOld = DeltaMove;
PlayerInput = _PlayerInput;
if (DeltaMove.y < 0)
{
DescendSlope(ref DeltaMove);
}
if (DeltaMove.y == 0)
{
Collisions.Below = true;
}
if (DeltaMove.x != 0)
{
Collisions.FaceDirection = (int)Mathf.Sign(DeltaMove.x);
Collisions.Dir = new Vector2(PlayerInput.x, PlayerInput.y);
if (Collisions.Dir.x > 0)
{
PlayerSprite.flipX = false;
}
else if (Collisions.Dir.x < 0)
{
PlayerSprite.flipX = true;
}
}
HorizontalCollision(ref DeltaMove);
if (DeltaMove.y != 0)
{
VerticalCollision(ref DeltaMove);
}
if (StandingOnPlatform)
{
Collisions.Below = true;
}
transform.Translate(DeltaMove);
}
private void HorizontalCollision(ref Vector2 DeltaMove)
{
float DirectionX = Collisions.FaceDirection;
float RayLength = Mathf.Abs(DeltaMove.x) + SkinWidth;
if (Mathf.Abs(DeltaMove.x) < SkinWidth)
{
RayLength = SkinWidth * 2;
}
for (int i = 0; i < HorizontalRayCount; i++)
{
Vector2 RayOrigin = (DirectionX == -1) ? RayCastOriginsCorners.BottomLeft : RayCastOriginsCorners.BottomRight;
RayOrigin += Vector2.up * (HorizontalRaySpacing * i);
RaycastHit2D RayCastHit = Physics2D.Raycast(RayOrigin, Vector2.right * DirectionX, RayLength, CollisionMask);
if (RayCastHit)
{
if (RayCastHit.distance == 0)
{
continue;
}
float SlopeAngle = Vector2.Angle(RayCastHit.normal, Vector2.up);
if (i == 0 && SlopeAngle <= MaxSlopeAngle)
{
if (Collisions.DescendingSlope)
{
Collisions.DescendingSlope = false;
DeltaMove = Collisions.DeltaMoveOld;
}
float DistanceToSlopeStart = 0;
if (SlopeAngle != Collisions.SlopeAngleOld)
{
DistanceToSlopeStart = RayCastHit.distance - SkinWidth;
DeltaMove.x -= DistanceToSlopeStart * DirectionX;
}
ClimbSlope(ref DeltaMove, SlopeAngle, RayCastHit.normal);
DeltaMove.x += DistanceToSlopeStart * DirectionX;
}
if (!Collisions.ClimbingSlope || SlopeAngle > MaxSlopeAngle)
{
DeltaMove.x = (RayCastHit.distance - SkinWidth) * DirectionX;
RayLength = RayCastHit.distance;
if (Collisions.ClimbingSlope)
{
DeltaMove.y = Mathf.Tan(Collisions.SlopeAngle * Mathf.Deg2Rad) * Mathf.Abs(DeltaMove.x);
}
Collisions.Left = DirectionX == -1;
Collisions.Right = DirectionX == 1;
}
}
}
}
private void VerticalCollision(ref Vector2 DeltaMove)
{
float DirectionY = Mathf.Sign(DeltaMove.y);
float RayLength = Mathf.Abs(DeltaMove.y) + SkinWidth;
for (int i = 0; i < VerticalRayCount; i++)
{
Vector2 RayOrigin = (DirectionY == -1) ? RayCastOriginsCorners.BottomLeft : RayCastOriginsCorners.TopLeft;
RayOrigin += Vector2.right * (VerticalRaySpacing * i + DeltaMove.x);
RaycastHit2D RayCastHit = Physics2D.Raycast(RayOrigin, Vector2.up * DirectionY, RayLength, CollisionMask);
if (RayCastHit)
{
if (RayCastHit.collider.CompareTag("Through"))
{
if (DirectionY == 1 || RayCastHit.distance == 0)
{
continue;
}
if (Collisions.FallingThroughPlatform)
{
continue;
}
if (PlayerInput.y == -1)
{
Collisions.FallingThroughPlatform = true;
Invoke("ResetFallingThroughPlatform", 0.5f);
continue;
}
}
DeltaMove.y = (RayCastHit.distance - SkinWidth) * DirectionY;
RayLength = RayCastHit.distance;
if (Collisions.ClimbingSlope)
{
DeltaMove.x = DeltaMove.y / Mathf.Tan(Collisions.SlopeAngle * Mathf.Deg2Rad) * Mathf.Sign(DeltaMove.x);
}
Collisions.Below = DirectionY == -1;
Collisions.Above = DirectionY == 1;
}
if (Collisions.ClimbingSlope)
{
float DirectionX = Mathf.Sign(DeltaMove.x);
RayLength = Mathf.Abs(DeltaMove.x) + SkinWidth;
Vector2 RayOrigin = ((DirectionX == -1) ? RayCastOriginsCorners.BottomLeft : RayCastOriginsCorners.BottomRight) + Vector2.up * DeltaMove.y;
RaycastHit2D RayCastHit = Physics2D.Raycast(RayOrigin, Vector2.right * DirectionX, RayLength, CollisionMask);
if (RayCastHit)
{
float SlopeAngle = Vector2.Angle(RayCastHit.normal, Vector2.up);
if (SlopeAngle != Collisions.SlopeAngle)
{
DeltaMove.x = (RayCastHit.distance - SkinWidth) * DirectionX;
Collisions.SlopeAngle = SlopeAngle;
Collisions.SlopeNormal = RayCastHit.normal;
}
}
}
}
private void DescendSlope(ref Vector2 DeltaMove)
{
RaycastHit2D MaxSlopeHitLeft = Physics2D.Raycast(RayCastOriginsCorners.BottomLeft, Vector2.down, Mathf.Abs(DeltaMove.y) + SkinWidth, CollisionMask);
RaycastHit2D MaxSlopeHitRight = Physics2D.Raycast(RayCastOriginsCorners.BottomRight, Vector2.down, Mathf.Abs(DeltaMove.y) + SkinWidth, CollisionMask);
if (MaxSlopeHitLeft ^ MaxSlopeHitRight)
{
SlideDownMaxSlope(MaxSlopeHitLeft, ref DeltaMove);
SlideDownMaxSlope(MaxSlopeHitRight, ref DeltaMove);
}
if (!Collisions.SlidingDownMaxSlope)
{
float DirectionX = Mathf.Sign(DeltaMove.x);
Vector2 RayOrigin = (DirectionX == 1) ? RayCastOriginsCorners.BottomRight : RayCastOriginsCorners.BottomLeft;
RaycastHit2D RayCastHit = Physics2D.Raycast(RayOrigin, -Vector2.up, Mathf.Infinity, CollisionMask);
if (RayCastHit)
{
float SlopeAngle = Vector2.Angle(RayCastHit.normal, Vector2.up);
if (SlopeAngle != 0 && SlopeAngle <= MaxSlopeAngle)
{
if (Mathf.Sign(RayCastHit.normal.x) == DirectionX)
{
if (RayCastHit.distance - SkinWidth <= Mathf.Tan(SlopeAngle * Mathf.Deg2Rad) * Mathf.Abs(DeltaMove.x))
{
float MoveDistance = Mathf.Abs(DeltaMove.x);
float DescendDeltaMoveY = Mathf.Sin(SlopeAngle * Mathf.Deg2Rad) * MoveDistance;
DeltaMove.x = Mathf.Cos(SlopeAngle * Mathf.Deg2Rad) * MoveDistance;
DeltaMove.y -= DescendDeltaMoveY;
Collisions.SlopeAngle = SlopeAngle;
Collisions.DescendingSlope = true;
Collisions.Below = true;
Collisions.SlopeNormal = RayCastHit.normal;
}
}
}
}
}
}
private void SlideDownMaxSlope(RaycastHit2D RayCastHit, ref Vector2 DeltaMove)
{
if (RayCastHit)
{
float SlopeAngle = Vector2.Angle(RayCastHit.normal, Vector2.up);
if (SlopeAngle > MaxSlopeAngle)
{
DeltaMove.x = RayCastHit.normal.x * (Mathf.Abs(DeltaMove.y) - RayCastHit.distance) / Mathf.Tan(SlopeAngle * Mathf.Deg2Rad);
Collisions.SlopeAngle = SlopeAngle;
Collisions.SlidingDownMaxSlope = true;
Collisions.SlopeNormal = RayCastHit.normal;
}
}
}
private void ClimbSlope(ref Vector2 DeltaMove, float SlopeAngle, Vector2 SlopeNormal)
{
float MoveDistance = Mathf.Abs(DeltaMove.x);
float ClimbDeltaMoveY = Mathf.Sin(SlopeAngle * Mathf.Deg2Rad) * MoveDistance;
if (DeltaMove.y <= ClimbDeltaMoveY)
{
DeltaMove.y = ClimbDeltaMoveY;
DeltaMove.x = Mathf.Cos(SlopeAngle * Mathf.Deg2Rad) * MoveDistance * Mathf.Sign(DeltaMove.x);
Collisions.Below = true;
Collisions.ClimbingSlope = true;
Collisions.SlopeAngle = SlopeAngle;
Collisions.SlopeNormal = SlopeNormal;
}
}
public Vector2 GetFaceDirection()
{
return Collisions.Dir;
}
}