본문 바로가기

IT/유니티

20. 유니티 교육 (Sebastian Lague 강의-2DRunner #2)



Controller2D.cs

using UnityEngine;

using System.Collections;


public class Controller2D : RaycastController {


float maxClimbAngle = 80;

float maxDescendingAngle = 75;



public CollisionInfo collisions;


[HideInInspector]

public Vector2 playerInput;


public override void Start(){

base.Start ();

collisions.faceDir = 1;

}


public void Move(Vector3 velocity, bool standingOnPlatform){

Move (velocity, Vector2.zero, standingOnPlatform);

}



public void Move(Vector3 velocity, Vector2 input, bool standingOnPlatform = false){

UpdateRaycastOrigins ();

collisions.Reset ();

collisions.velocityOld = velocity;

playerInput = input;


if (velocity.x != 0) {

collisions.faceDir = (int)Mathf.Sign (velocity.x);

}


if(velocity.y < 0){

DescendSlope (ref velocity);

}

//좌우충돌체크를 한다음에 상하충돌체크를 해야한다.

//왜냐하면 VerticalCollisions함수에서 rayOrigin에 velocity.x를 더하기때문이다. 

HorizontalCollisions (ref velocity);


if(velocity.y != 0){

VerticalCollisions (ref velocity);

}


  transform.Translate (velocity);


if(standingOnPlatform){

collisions.below = true;

}

}


void HorizontalCollisions(ref Vector3 velocity){

float directionX = collisions.faceDir;

float rayLenth = Mathf.Abs (velocity.x) + skinWidth;


if (Mathf.Abs (velocity.x) < skinWidth) {

rayLenth = 2 * skinWidth;

}


for(int i = 0; i < horizontalRayCount; i++){

Vector2 rayOrigin = (directionX == -1) ? raycastOrigins.bottomLeft : raycastOrigins.bottomRight;

rayOrigin += Vector2.up * (horizontalRaySpacing * i);

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, Vector2.right * directionX, rayLenth, collisionMask);

Debug.DrawRay (rayOrigin, Vector2.right* directionX *rayLenth, Color.red);

if(hit){

if (hit.distance == 0) {

continue;

}


float slopeAngle = Vector2.Angle (hit.normal, Vector2.up);

if (i == 0 && slopeAngle <= maxClimbAngle) {

if(collisions.descendingSlope){

collisions.descendingSlope = false;

velocity = collisions.velocityOld;

}

float distanceToSlopeStart = 0;

if(slopeAngle != collisions.slopeAngleOld){

distanceToSlopeStart = hit.distance - skinWidth;

velocity.x -= distanceToSlopeStart * directionX;

}

ClimbSlope (ref velocity, slopeAngle);

velocity.x += distanceToSlopeStart * directionX;

}


if(!collisions.climbingSlope || slopeAngle > maxClimbAngle){

velocity.x = (hit.distance - skinWidth) * directionX;

rayLenth = hit.distance;


if (collisions.climbingSlope) {

velocity.y = Mathf.Tan (collisions.slopeAngle * Mathf.Deg2Rad)*Mathf.Abs(velocity.x);

}


collisions.left = directionX == -1;

collisions.right = directionX == 1;

}



}


}

}



void VerticalCollisions(ref Vector3 velocity){

float directionY = Mathf.Sign (velocity.y);

float rayLenth = Mathf.Abs (velocity.y) + skinWidth;

for(int i = 0; i < verticalRayCount; i++){

Vector2 rayOrigin = (directionY == -1) ? raycastOrigins.bottomLeft : raycastOrigins.topLeft;

rayOrigin += Vector2.right * (verticalRaySpacing * i + velocity.x);

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, Vector2.up * directionY, rayLenth, collisionMask);

Debug.DrawRay (rayOrigin, Vector2.up* directionY *rayLenth, Color.red);


if(hit){

if(hit.collider.tag == "Through"){

if (directionY == 1 || hit.distance == 0) {

continue;

}

if(collisions.fallingThroughPlatform == true){

continue;

}

if(playerInput.y == -1){

collisions.fallingThroughPlatform = true;

Invoke ("ResetFallingthroughPlatform", .5f);

continue;

}

}


velocity.y = (hit.distance - skinWidth) * directionY;

rayLenth = hit.distance;


if (collisions.climbingSlope) {

velocity.x = velocity.y / Mathf.Tan(collisions.slopeAngle * Mathf.Deg2Rad) * Mathf.Sign(velocity.x);

}


collisions.below = directionY == -1;

collisions.above = directionY == 1;

}


}


if (collisions.climbingSlope) {

float directionX = Mathf.Sign (velocity.x);

rayLenth = Mathf.Abs (velocity.x) + skinWidth;

Vector2 rayOrigin = ((directionX == -1) ? raycastOrigins.bottomLeft : raycastOrigins.bottomRight) + Vector2.up * velocity.y;

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, Vector2.right * directionX, rayLenth, collisionMask);


if(hit){

float slopeAngle = Vector2.Angle (hit.normal, Vector2.up);

if(slopeAngle != collisions.slopeAngle){

velocity.x = (hit.distance - skinWidth) * directionX;

collisions.slopeAngle = slopeAngle;

}

}

}

}


void ClimbSlope(ref Vector3 velocity, float slopeAngle){

float moveDistance = Mathf.Abs (velocity.x);

float climbVelocityY = Mathf.Sin (slopeAngle*Mathf.Deg2Rad) * moveDistance;


if (velocity.y <= climbVelocityY) {

velocity.y = climbVelocityY;

velocity.x = Mathf.Cos (slopeAngle*Mathf.Deg2Rad) * moveDistance * Mathf.Sign(velocity.x);

collisions.below = true;

collisions.climbingSlope = true;

collisions.slopeAngle = slopeAngle;

}



}


void DescendSlope(ref Vector3 velocity){

float directionX = Mathf.Sign (velocity.x);

Vector2 rayOrigin = (directionX == -1) ? raycastOrigins.bottomRight : raycastOrigins.bottomLeft;

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, - Vector2.up, Mathf.Infinity, collisionMask);


if (hit) {

float slopeAngle = Vector2.Angle (hit.normal, Vector2.up);

if(slopeAngle != 0 && slopeAngle <= maxDescendingAngle){

if (Mathf.Sign (hit.normal.x) == directionX) {

if (hit.distance - skinWidth <= Mathf.Tan (slopeAngle * Mathf.Deg2Rad) * Mathf.Abs (velocity.x)) {

float moveDistance = Mathf.Abs (velocity.x);

float descendVelocityY = Mathf.Sin (slopeAngle*Mathf.Deg2Rad) * moveDistance;

velocity.x = Mathf.Cos (slopeAngle*Mathf.Deg2Rad) * moveDistance * Mathf.Sign(velocity.x);

velocity.y -= descendVelocityY;

collisions.slopeAngle = slopeAngle;

collisions.descendingSlope = true;

collisions.below = true;

}

}

}

}

}


void ResetFallingthroughPlatform(){

collisions.fallingThroughPlatform = false;

}




public struct CollisionInfo{

public bool above, below;

public bool left, right;


public bool climbingSlope;

public bool descendingSlope;

public float slopeAngle, slopeAngleOld;

public int faceDir;

public bool fallingThroughPlatform;


public Vector3 velocityOld;


public void Reset(){

above = below = false;

left = right = false;

climbingSlope = false;

descendingSlope = false;


slopeAngleOld = slopeAngle;

slopeAngle = 0;

}


}

}



PlatFormController.cs

using UnityEngine;

using System.Collections;

using System.Collections.Generic;


public class PlatFormController : RaycastController {


public LayerMask passengerMask;



public Vector3[] localWayPoints;

Vector3[] globalWayPoints;


public float speed;

public bool cyclic;

public float waitTime;


[Range(0,2)]

public float easeAmount;


int fromWaypointIndex;

float percentBetweenWaypoints;

float nextMoveTime;


List<PassengerMovement> passengerMovement;

Dictionary<Transform, Controller2D> passengerDictionary = new Dictionary<Transform, Controller2D>();


// Use this for initialization

public override void Start () {

base.Start ();


globalWayPoints = new Vector3[localWayPoints.Length];

for (int i = 0; i < localWayPoints.Length; i++) {

globalWayPoints [i] = localWayPoints [i] + transform.position;

}

}

// Update is called once per frame

void Update () {

UpdateRaycastOrigins ();


Vector3 velocity = CalculatePlatformMovement();


CalculatePassengerMovement (velocity);


MovePassengers (true);

transform.Translate (velocity);

MovePassengers (false);

  }


float Ease(float x){

float a = easeAmount + 1;

return Mathf.Pow (x, a) / (Mathf.Pow (x, a) + Mathf.Pow (1 - x, a));

}


Vector3 CalculatePlatformMovement(){


if (Time.time < nextMoveTime) {

return Vector3.zero;

}


fromWaypointIndex %= globalWayPoints.Length;


int toWaypointIndex = (fromWaypointIndex + 1) % globalWayPoints.Length;

float distanceBetweenWaypoints = Vector3.Distance (globalWayPoints[fromWaypointIndex], globalWayPoints[toWaypointIndex]);

percentBetweenWaypoints += Time.deltaTime * speed/distanceBetweenWaypoints;

percentBetweenWaypoints = Mathf.Clamp01 (percentBetweenWaypoints);

float easedPercentBetweenWaypoints = Ease (percentBetweenWaypoints);


Vector3 newPos = Vector3.Lerp (globalWayPoints [fromWaypointIndex], globalWayPoints [toWaypointIndex], easedPercentBetweenWaypoints);


if(percentBetweenWaypoints >= 1){

percentBetweenWaypoints = 0;

fromWaypointIndex++;

if(!cyclic){

if (fromWaypointIndex >= globalWayPoints.Length - 1) {

fromWaypointIndex = 0;

System.Array.Reverse (globalWayPoints);

}

}


nextMoveTime = Time.time + waitTime;

}


return newPos - transform.position;

}



void MovePassengers(bool beforeMovePlatform){

foreach(PassengerMovement passenger in passengerMovement){

if (!passengerDictionary.ContainsKey (passenger.transform)) {

passengerDictionary.Add (passenger.transform, passenger.transform.GetComponent<Controller2D>());

}

if(passenger.moveBeforePlatform == beforeMovePlatform){

passengerDictionary[passenger.transform].Move (passenger.velocity, passenger.standingOnPlatform);

}

}

}


void CalculatePassengerMovement(Vector3 velocity){

HashSet<Transform> movedPassengers = new HashSet<Transform> ();

passengerMovement = new List<PassengerMovement> ();


float directionX = Mathf.Sign (velocity.x);

float directionY = Mathf.Sign (velocity.y);


// Vertically moving

if (velocity.y != 0) {

float rayLenth = Mathf.Abs (velocity.y) + skinWidth;

for (int i = 0; i < verticalRayCount; i++) {

Vector2 rayOrigin = (directionY == -1) ? raycastOrigins.bottomLeft : raycastOrigins.topLeft;

rayOrigin += Vector2.right * (verticalRaySpacing * i);

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, Vector2.up * directionY, rayLenth, passengerMask);


if (hit && hit.distance != 0) {

if(!movedPassengers.Contains(hit.transform)){

movedPassengers.Add (hit.transform);

float pushX = (directionY == 1) ? velocity.x : 0f;

float pushY = velocity.y - (hit.distance - skinWidth) * directionY;


passengerMovement.Add (new PassengerMovement (hit.transform, new Vector3(pushX, pushY),

directionY == 1, true));

}


}

}

}


// horizontally moving

if(velocity.x != 0){

float rayLenth = Mathf.Abs (velocity.x) + skinWidth;

for (int i = 0; i < horizontalRayCount; i++) {

Vector2 rayOrigin = (directionX == -1) ? raycastOrigins.bottomLeft : raycastOrigins.bottomRight;

rayOrigin += Vector2.up * (horizontalRaySpacing * i);

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, Vector2.right * directionX, rayLenth, passengerMask);

if (hit && hit.distance != 0) {

if (!movedPassengers.Contains (hit.transform)) {

movedPassengers.Add (hit.transform);

float pushX = velocity.x - (hit.distance - skinWidth) * directionX;

float pushY = -skinWidth;


passengerMovement.Add (new PassengerMovement (hit.transform, new Vector3(pushX, pushY),

false, true));

}


}

}

}


//passenger on top of a horizontally or downward moving platform

if(directionY == -1 || velocity.y == 0 && velocity.x != 0){


float rayLenth = skinWidth *2;


for (int i = 0; i < verticalRayCount; i++) {

Vector2 rayOrigin = raycastOrigins.topLeft + Vector2.right * (verticalRaySpacing * i);

RaycastHit2D hit = Physics2D.Raycast (rayOrigin, Vector2.up, rayLenth, passengerMask);


if (hit && hit.distance != 0) {

if(!movedPassengers.Contains(hit.transform)){

movedPassengers.Add (hit.transform);

float pushX = velocity.x;

float pushY = velocity.y;


passengerMovement.Add (new PassengerMovement (hit.transform, new Vector3(pushX, pushY),

true, false));

}


}

}



}


}


struct PassengerMovement{

public Transform transform;

public Vector3 velocity;

public bool standingOnPlatform;

public bool moveBeforePlatform;


public PassengerMovement(Transform _transform, Vector3 _velocity, bool _standingOnPlatform, bool _moveBeforePlatform){

transform = _transform;

velocity = _velocity;

standingOnPlatform = _standingOnPlatform;

moveBeforePlatform = _moveBeforePlatform;


}

}


void OnDrawGizmos(){

if(localWayPoints != null){

Gizmos.color = Color.red;

float size = .3f;


for(int i = 0; i < localWayPoints.Length; i++){

Vector3 globalWayPointPos = (Application.isPlaying)?globalWayPoints[i] : localWayPoints [i] + transform.position;

Gizmos.DrawLine (globalWayPointPos - Vector3.up * size, globalWayPointPos + Vector3.up * size);

Gizmos.DrawLine (globalWayPointPos - Vector3.left * size, globalWayPointPos + Vector3.left * size);

}

}

}

}




CameraFallow.cs

using UnityEngine;

using System.Collections;


public class CameraFallow : MonoBehaviour {

public Controller2D target;

public float verticalOffset;

public float lookAheadDstX;

public float lookSmoothTimeX;

public float verticalSmoothTime;

public Vector2 focusAreaSize;


FocusArea focusArea; 


float currentLookAheadX;

float targetLookAheadX;

float lookAheadDirX;

float smoothLookVelocityX;

float smoothVelocityY;


bool lookAheadStopped;


void Start(){

focusArea = new FocusArea (target.collider.bounds, focusAreaSize);

}


void LateUpdate(){

focusArea.Update (target.collider.bounds);


Vector2 focusPosition = focusArea.center + Vector2.up * verticalOffset;


if (focusArea.velocity.x != 0) {

lookAheadDirX = Mathf.Sign (focusArea.velocity.x);

if (Mathf.Sign (target.playerInput.x) == Mathf.Sign (focusArea.velocity.x) && target.playerInput.x != 0) {

lookAheadStopped = false;

targetLookAheadX = lookAheadDirX * lookAheadDstX;

} else {

if(!lookAheadStopped){

lookAheadStopped = true;

targetLookAheadX = currentLookAheadX + (lookAheadDirX * lookAheadDstX - currentLookAheadX)/4f;

}


}

}


currentLookAheadX = Mathf.SmoothDamp (currentLookAheadX, targetLookAheadX, ref smoothLookVelocityX, lookSmoothTimeX);


focusPosition.y = Mathf.SmoothDamp (transform.position.y, focusPosition.y, ref smoothVelocityY, verticalSmoothTime);

focusPosition += Vector2.right * currentLookAheadX;

transform.position = (Vector3)focusPosition + Vector3.forward * -10;

}



void OnDrawGizmos(){

Gizmos.color = new Color (1, 0, 0, .5f);

Gizmos.DrawCube (focusArea.center, focusAreaSize);


}


struct FocusArea{

public Vector2 center;

public Vector2 velocity;

float left, right;

float top, bottom;


public FocusArea(Bounds targetBounds, Vector2 size){

left = targetBounds.center.x - size.x/2;

right = targetBounds.center.x + size.x/2;

bottom = targetBounds.min.y;

top = targetBounds.min.y + size.y;


velocity = Vector2.zero;

center = new Vector2((left + right)/2, (top+bottom)/2);


}


public void Update(Bounds targetBounds){

float shiftX = 0;

if(targetBounds.min.x < left){

shiftX = targetBounds.min.x - left;

}else if(targetBounds.max.x > right){

shiftX = targetBounds.max.x - right;

}

left += shiftX;

right += shiftX;


float shiftY= 0;

if(targetBounds.min.y < bottom){

shiftY = targetBounds.min.y - bottom;

}else if(targetBounds.max.y > top){

shiftY = targetBounds.max.y - top;

}

top += shiftY;

bottom += shiftY;

center = new Vector2((left + right)/2, (top+bottom)/2);

velocity = new Vector2 (shiftX, shiftY);

}

}


}