Unity game moving platform + respawning troubles
[Using Unity 5.3.1 Personal Edition]
For anyone that wants to test it out for himself:
http://www.mediafire.com/download/3k...tched+game.exe
So I've made a 2D platformer game with moving platforms and respawning if you've fallen off the map.
My problem is, that when I respawn the passenger mask on my moving platform doesn't work anymore (the passenger mask makes the player move along with the platform, so basically I'll fall through a platform that's moving upward).
I have assigned the correct tags/layers to all my platforms and player, I'm using "Environment" for the platforms and "Player" for the player.
Oh and I'll give you all my TC and/or a kiss if you find a way for me to fix this
C# code:
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>();
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;
}
}
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 platform
if (velocity.y != 0)
{
float rayLength = 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, rayLength, passengerMask);
if (hit && hit.distance != 0)
{
if (!movedPassengers.Contains(hit.transform))
{
movedPassengers.Add(hit.transform);
float pushX = (directionY == 1) ? velocity.x : 0;
float pushY = velocity.y - (hit.distance - skinWidth) * directionY;
passengerMovement.Add(new PassengerMovement(hit.transform, new Vector3(pushX, pushY), directionY == 1, true));
}
}
}
}
// Horizontally moving platform
if (velocity.x != 0)
{
float rayLength = 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, rayLength, 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 rayLength = skinWidth * 2;
for (int i = 0; i < verticalRayCount; i++)
{
Vector2 rayOrigin = raycastOrigins.topLeft + Vector2.right * (verticalRaySpacing * i);
RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up, rayLength, 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);
}
}
}
}
C# code:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Controller2D))]
public class Player : MonoBehaviour
{
[System.Serializable]
public class PlayerStats
{
public int Health = 100;
}
public PlayerStats playerStats = new PlayerStats();
public int fallBoundary = -20;
public void DamagePlayer (int damage)
{
playerStats.Health -= damage;
if (playerStats.Health <= 0)
{
GameMaster.KillPlayer(this);
}
}
public float maxJumpHeight = 20;
public float minJumpHeight = 1;
public float timeToJumpApex = .4f;
float accelerationTimeAirborne = .2f;
float accelerationTimeGrounded = .1f;
float moveSpeed = 10;
public Vector2 wallJumpClimb;
public Vector2 wallJumpOff;
public Vector2 wallLeap;
public float wallSlideSpeedMax = 3;
public float wallStickTime = .25f;
float timeToWallUnstick;
float gravity;
float maxJumpVelocity;
float minJumpVelocity;
Vector3 velocity;
float velocityXSmoothing;
Controller2D controller;
void Start()
{
controller = GetComponent<Controller2D>();
gravity = -(2 * maxJumpHeight) / Mathf.Pow(timeToJumpApex, 2);
maxJumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
minJumpVelocity = Mathf.Sqrt(2 * Mathf.Abs(gravity) * minJumpHeight);
print("Gravity: " + gravity + " Jump Velocity: " + maxJumpVelocity);
}
void Update()
{
{
if (transform.position.y <= fallBoundary)
DamagePlayer(9999999);
}
Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
int wallDirX = (controller.collisions.left) ? -1 : 1;
float targetVelocityX = input.x * moveSpeed;
velocity.x = Mathf.SmoothDamp(velocity.x, targetVelocityX, ref velocityXSmoothing, (controller.collisions.below) ? accelerationTimeGrounded : accelerationTimeAirborne);
bool wallSliding = false;
if ((controller.collisions.left || controller.collisions.right) && !controller.collisions.below && velocity.y < 0)
{
wallSliding = true;
if (velocity.y < -wallSlideSpeedMax)
{
velocity.y = -wallSlideSpeedMax;
}
if (timeToWallUnstick > 0)
{
velocityXSmoothing = 0;
velocity.x = 0;
if (input.x != wallDirX && input.x != 0)
{
timeToWallUnstick -= Time.deltaTime;
}
else {
timeToWallUnstick = wallStickTime;
}
}
else {
timeToWallUnstick = wallStickTime;
}
}
if (Input.GetKeyDown(KeyCode.Space))
{
if (wallSliding)
{
if (wallDirX == input.x)
{
velocity.x = -wallDirX * wallJumpClimb.x;
velocity.y = wallJumpClimb.y;
}
else if (input.x == 0)
{
velocity.x = -wallDirX * wallJumpOff.x;
velocity.y = wallJumpOff.y;
}
else {
velocity.x = -wallDirX * wallLeap.x;
velocity.y = wallLeap.y;
}
}
if (controller.collisions.below)
{
velocity.y = maxJumpVelocity;
}
}
if (Input.GetKeyUp(KeyCode.Space))
{
if (velocity.y > minJumpVelocity)
{
velocity.y = minJumpVelocity;
}
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime, input);
if (controller.collisions.above || controller.collisions.below)
{
velocity.y = 0;
}
}
}
C# code:
using UnityEngine;
using System.Collections;
public class GameMaster : MonoBehaviour {
public static GameMaster gm;
void Start() {
if (gm == null)
{
gm = GameObject.FindGameObjectWithTag("GM").GetComponent<GameMaster>();
}
}
public Transform playerPrefab;
public Transform spawnPoint;
public int spawnDelay = 2;
public IEnumerator RespawnPlayer ()
{
yield return new WaitForSeconds(spawnDelay);
Instantiate(playerPrefab, spawnPoint.position, spawnPoint.rotation);
}
public static void KillPlayer(Player player)
{
Destroy(player.gameObject);
gm.StartCoroutine(gm.RespawnPlayer());
}
}
C# code:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(BoxCollider2D))]
public class RaycastController : MonoBehaviour
{
public LayerMask collisionMask;
public const float skinWidth = .015f;
public int horizontalRayCount = 4;
public int verticalRayCount = 4;
[HideInInspector]
public float horizontalRaySpacing;
[HideInInspector]
public float verticalRaySpacing;
[HideInInspector]
public BoxCollider2D collider;
public RaycastOrigins raycastOrigins;
public virtual void Awake()
{
collider = GetComponent<BoxCollider2D>();
}
public virtual void Start()
{
CalculateRaySpacing();
}
public void UpdateRaycastOrigins()
{
Bounds bounds = collider.bounds;
bounds.Expand(skinWidth * -2);
raycastOrigins.bottomLeft = new Vector2(bounds.min.x, bounds.min.y);
raycastOrigins.bottomRight = new Vector2(bounds.max.x, bounds.min.y);
raycastOrigins.topLeft = new Vector2(bounds.min.x, bounds.max.y);
raycastOrigins.topRight = new Vector2(bounds.max.x, bounds.max.y);
}
public void CalculateRaySpacing()
{
Bounds bounds = collider.bounds;
bounds.Expand(skinWidth * -2);
horizontalRayCount = Mathf.Clamp(horizontalRayCount, 2, int.MaxValue);
verticalRayCount = Mathf.Clamp(verticalRayCount, 2, int.MaxValue);
horizontalRaySpacing = bounds.size.y / (horizontalRayCount - 1);
verticalRaySpacing = bounds.size.x / (verticalRayCount - 1);
}
public struct RaycastOrigins
{
public Vector2 topLeft, topRight;
public Vector2 bottomLeft, bottomRight;
}
}
C# code:
using UnityEngine;
using System.Collections;
public class Controller2D : RaycastController
{
float maxClimbAngle = 80;
float maxDescendAngle = 80;
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);
}
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 rayLength = Mathf.Abs(velocity.x) + skinWidth;
if (Mathf.Abs(velocity.x) < skinWidth)
{
rayLength = 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, rayLength, collisionMask);
Debug.DrawRay(rayOrigin, Vector2.right * directionX * rayLength, 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;
rayLength = 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 rayLength = 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, rayLength, collisionMask);
Debug.DrawRay(rayOrigin, Vector2.up * directionY * rayLength, Color.red);
if (hit)
{
if (hit.collider.tag == "Through")
{
if (directionY == 1 || hit.distance == 0)
{
continue;
}
if (collisions.fallingThroughPlatform)
{
continue;
}
if (playerInput.y == -1)
{
collisions.fallingThroughPlatform = true;
Invoke("ResetFallingThroughPlatform", .5f);
continue;
}
}
velocity.y = (hit.distance - skinWidth) * directionY;
rayLength = 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);
rayLength = 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, rayLength, 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 <= maxDescendAngle)
{
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 Vector3 velocityOld;
public int faceDir;
public bool fallingThroughPlatform;
public void Reset()
{
above = below = false;
left = right = false;
climbingSlope = false;
descendingSlope = false;
slopeAngleOld = slopeAngle;
slopeAngle = 0;
}
}
}
C# code:
using UnityEngine;
namespace UnitySampleAssets._2D
{
public class Camera2DFollow : MonoBehaviour
{
public Transform target;
public float damping = 1;
public float lookAheadFactor = 3;
public float lookAheadReturnSpeed = 0.5f;
public float lookAheadMoveThreshold = 0.1f;
public float yPosRestriction = -1;
private float offsetZ;
private Vector3 lastTargetPosition;
private Vector3 currentVelocity;
private Vector3 lookAheadPos;
float nextTimeToSearch = 0;
// Use this for initialization
private void Start()
{
lastTargetPosition = target.position;
offsetZ = (transform.position - target.position).z;
transform.parent = null;
}
// Update is called once per frame
private void Update()
{
if (target == null)
{
FindPlayer();
return;
}
// only update lookahead pos if accelerating or changed direction
float xMoveDelta = (target.position - lastTargetPosition).x;
bool updateLookAheadTarget = Mathf.Abs(xMoveDelta) > lookAheadMoveThreshold;
if (updateLookAheadTarget)
{
lookAheadPos = lookAheadFactor*Vector3.right*Mathf.Sign(xMoveDelta);
}
else
{
lookAheadPos = Vector3.MoveTowards(lookAheadPos, Vector3.zero, Time.deltaTime*lookAheadReturnSpeed);
}
Vector3 aheadTargetPos = target.position + lookAheadPos + Vector3.forward*offsetZ;
Vector3 newPos = Vector3.SmoothDamp(transform.position, aheadTargetPos, ref currentVelocity, damping);
newPos = new Vector3(newPos.x, Mathf.Clamp (newPos.y, yPosRestriction, Mathf.Infinity), newPos.z);
transform.position = newPos;
lastTargetPosition = target.position;
}
void FindPlayer ()
{
if (nextTimeToSearch <= Time.time)
{
GameObject searchResult = GameObject.FindGameObjectWithTag("Player");
if (searchResult != null)
target = searchResult.transform;
nextTimeToSearch = Time.time + 0.5f;
}
}
}
}
Last edited by dank; Feb 12, 2016 at 12:22 AM.