Skip to content

Instantly share code, notes, and snippets.

@CoffeepotCoder
Last active March 10, 2022 03:51
Show Gist options
  • Save CoffeepotCoder/5fb5b4b2ffdd0a86ced3163921f95468 to your computer and use it in GitHub Desktop.
Save CoffeepotCoder/5fb5b4b2ffdd0a86ced3163921f95468 to your computer and use it in GitHub Desktop.
Chronobelt: Into the Beyond
using UnityEngine;
/// <summary>
/// Defines the dimensions of the belt, and populates it with rubble
/// </summary>
public class RubbleBelt : MonoBehaviour
{
[Header("Orbit Config")]
[SerializeField] CircleCollider2D _collider;
[SerializeField] OrbitalLine _orbitalLine;
[SerializeField] float _radiusOffset;
[SerializeField] float _posVariance;
[SerializeField] float _orbitSpeed = 0.25f;
[SerializeField] float _orbitSpeedMax = 2.75f;
[SerializeField] float _orbitSpeedStep = 0.5f;
[SerializeField] int _rubbleInOrbit;
[SerializeField] GameObject[] _rubblePrefabs;
void Start()
{
var radius = GetComponent<CircleCollider2D>().radius + _radiusOffset;
var degree = 360 / _rubbleInOrbit;
GetComponent<OrbitalLine>().DrawCircle(radius);
for (int i = 0; i <= _rubbleInOrbit; i++)
{
var spoke = new GameObject($"Rubble ({i})");
spoke.transform.parent = transform;
spoke.transform.localPosition = Vector3.zero;
spoke.transform.localEulerAngles = new Vector3(0, 0, degree * i);
var rubble = Instantiate(_rubblePrefabs[Random.Range(0, _rubblePrefabs.Length)], spoke.transform);
rubble.transform.localPosition = new Vector3(Random.Range(-_posVariance, _posVariance), Random.Range(-_posVariance, _posVariance) + radius, 1f);
}
transform.localEulerAngles = Vector3.forward * Random.Range(0, 360);
var step = Mathf.CeilToInt(_orbitSpeedMax / _orbitSpeedStep);
_orbitSpeed = _orbitSpeedStep * Random.Range(-step, step);
if (_orbitSpeed == 0 || _orbitSpeed % 0.5f == 0)
_orbitSpeed += 0.25f;
}
void Update()
=> transform.Rotate(0, 0, _orbitSpeed * Time.deltaTime);
void OnMouseOver()
{
_orbitSpeed = Mathf.Clamp(_orbitSpeed - Input.mouseScrollDelta.y * _orbitSpeedStep, -_orbitSpeedMax, _orbitSpeedMax);
if (_orbitSpeed == 0f)
_orbitSpeed = 0.25f * -Input.mouseScrollDelta.y;
}
void OnValidate()
{
_collider = GetComponent<CircleCollider2D>();
_orbitalLine = GetComponent<OrbitalLine>();
_orbitalLine.DrawCircle(_collider.radius + _radiusOffset);
}
}
using UnityEngine;
/// <summary>
/// Self-disabling and Self-randomizing space rocks
/// </summary>
public class Rubble : MonoBehaviour
{
Vector3 _rotationStep;
SpriteRenderer _renderer;
CircleCollider2D _collider;
void OnEnable()
{
_collider = GetComponent<CircleCollider2D>();
_renderer = GetComponent<SpriteRenderer>();
_renderer.flipX = Random.Range(0, 2) == 0;
transform.localScale = Vector3.one * Random.Range(0.75f, 1.25f);
_rotationStep = Vector3.forward * Random.Range(-1f, 1f);
}
void FixedUpdate()
{
if (_renderer.isVisible)
transform.Rotate(_rotationStep);
}
void OnBecameInvisible()
=> _collider.enabled = false;
void OnBecameVisible()
=> _collider.enabled = true;
}
using UnityEngine;
/// <summary>
/// Draws a circle along the RubbleBelt orbital radius as an interactive visual guide for the player
/// </summary>
[RequireComponent(typeof(LineRenderer))]
public class OrbitalLine : MonoBehaviour
{
public void DrawCircle(float radius)
{
var segments = 360;
var line = gameObject.GetComponent<LineRenderer>();
line.useWorldSpace = false;
line.positionCount = segments + 1;
var pointCount = segments + 1; // add extra point to make startpoint and endpoint the same to close the circle
var points = new Vector3[pointCount];
for (int i = 0; i < pointCount; i++)
{
var rad = Mathf.Deg2Rad * (i * 360f / segments);
points[i] = new Vector3(Mathf.Sin(rad) * radius, Mathf.Cos(rad) * radius, 0);
}
line.SetPositions(points);
}
}
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Deploys colonists along their forward vector at a variable interval
/// Displays remaining colonists with a text UI element
/// </summary>
public class ColonistSpawner : MonoBehaviour
{
[Header("Dependencies")]
[SerializeField] GameObject _colonistPrefab;
[SerializeField] AudioClip[] _deploymentClips;
[SerializeField] AudioSource _source;
[SerializeField] Text _colonistText;
[Header("Configuration")]
[SerializeField] float _mouseDeltaSensitivity = 0.25f;
[SerializeField] float _colonistSpeedMin = .5f;
[SerializeField] float _colonistSpeedMax = 2.5f;
[SerializeField] float _colonistSpeed = 1f;
[SerializeField] float _spawnInterval = 5f;
[SerializeField] internal int _colonistsRemaining = 10;
float _spawnTimer;
void OnMouseOver()
{
if (Input.mouseScrollDelta != Vector2.zero)
{
_colonistSpeed = Mathf.Clamp(_colonistSpeed + Input.mouseScrollDelta.y * _mouseDeltaSensitivity, _colonistSpeedMin, _colonistSpeedMax);
var colonists = GetComponentsInChildren<Rigidbody2D>();
foreach (var colonist in colonists)
colonist.velocity = colonist.transform.up * _colonistSpeed;
}
}
void Start()
=> _spawnTimer = Random.Range(0f, _colonistSpeedMin);
void Update()
{
if (_colonistsRemaining > 0 && (_spawnTimer += Time.deltaTime) >= _spawnInterval / _colonistSpeed)
{
_colonistText.text = $"{--_colonistsRemaining}";
_source.PlayOneShot(_deploymentClips[Random.Range(0, _deploymentClips.Length)]);
_spawnTimer = Random.Range(0f, 0.25f);
var colonist = Instantiate(_colonistPrefab, transform);
colonist.transform.Rotate(Vector3.forward * Random.Range(-2.5f, 2.5f));
var rb = colonist.GetComponent<Rigidbody2D>();
rb.velocity = colonist.transform.up * _colonistSpeed;
colonist.transform.localPosition = Vector3.forward;
}
}
}
using UnityEngine;
/// <summary>
/// Colonists that reach the surface contribute to player score
/// </summary>
public class Colonist : MonoBehaviour
{
[SerializeField] GameObject _explosionPrefab;
[SerializeField] SpriteRenderer _renderer;
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Planet"))
{
FindObjectOfType<ScoreKeeper>().AddScore();
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
transform.SetParent(collision.gameObject.transform);
Destroy(gameObject, 1f);
return;
}
Instantiate(_explosionPrefab, transform.position, Quaternion.identity);
Destroy(gameObject);
}
void OnCollisionStay2D(Collision2D collision)
=> _renderer.color = Color.Lerp(_renderer.color, Color.clear, Time.deltaTime);
}
using UnityEngine;
/// <summary>
/// Draws a line along the forward vector of the Spawner as an interactive visual guide for the player
/// </summary>
public class TrajectoryLine : MonoBehaviour
{
[SerializeField] float _lineLength = 2.5f;
[SerializeField] LineRenderer _line;
void Start()
{
_line.SetPosition(0, transform.position);
_line.SetPosition(1, transform.position + transform.up * _lineLength);
}
void OnValidate()
{
_line = GetComponent<LineRenderer>();
Start();
}
}
using UnityEngine;
/// <summary>
/// Emphasizes LineRenderer when under the mouse pointer
/// </summary>
[RequireComponent(typeof(LineRenderer))]
public class LineMouseModifier : MonoBehaviour
{
[SerializeField] Color _mouseOverColorStart;
[SerializeField] Color _mouseOverColorEnd;
[SerializeField] float _mouseOverWidth;
[SerializeField] Color _mouseExitColorStart;
[SerializeField] Color _mouseExitColorEnd;
[SerializeField] float _mouseExitWidth;
[SerializeField] float _lerpRate = 2.5f;
Color _targetColorStart, _targetColorEnd = Color.clear;
float _targetWidth = 0f;
LineRenderer _line;
void Start()
{
_line = GetComponent<LineRenderer>();
_targetColorStart = _mouseExitColorStart;
_targetColorEnd = _mouseExitColorEnd;
_targetWidth = _mouseExitWidth;
}
void Update()
{
_line.startColor = Color.Lerp(_line.startColor, _targetColorStart, _lerpRate * Time.deltaTime);
_line.endColor = Color.Lerp(_line.endColor, _targetColorEnd, _lerpRate * Time.deltaTime);
_line.startWidth = Mathf.Lerp(_line.startWidth, _targetWidth, _lerpRate * Time.deltaTime);
_line.endWidth = Mathf.Lerp(_line.endWidth, _targetWidth, _lerpRate * Time.deltaTime);
}
void OnMouseEnter()
{
_targetColorStart = _mouseOverColorStart;
_targetColorEnd = _mouseOverColorEnd;
_targetWidth = _mouseOverWidth;
}
void OnMouseExit()
{
_targetColorStart = _mouseExitColorStart;
_targetColorEnd = _mouseExitColorEnd;
_targetWidth = _mouseExitWidth;
}
}
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
/// <summary>
/// Tracks and displays score
/// </summary>
public class ScoreKeeper : MonoBehaviour
{
ColonistSpawner[] _spawners;
int _totalColonists;
int _colonistsSaved = 0;
int _highScore;
Text _scoreText;
void Start()
{
_spawners = FindObjectsOfType<ColonistSpawner>();
foreach (var spawner in _spawners)
_totalColonists += spawner._colonistsRemaining;
_scoreText = GetComponent<Text>();
_highScore = PlayerPrefs.GetInt(SceneManager.GetActiveScene().name, 0);
_scoreText.text = $"{_colonistsSaved:00} / {_totalColonists:00} (Best: {_highScore:00} / {_totalColonists:00}";
}
void OnDisable()
{
if (_colonistsSaved > PlayerPrefs.GetInt(SceneManager.GetActiveScene().name, 0))
PlayerPrefs.SetInt(SceneManager.GetActiveScene().name, _colonistsSaved);
}
internal void AddScore()
{
if (++_colonistsSaved > _highScore)
_highScore = _colonistsSaved;
_scoreText.text = $"{_colonistsSaved:00} / {_totalColonists:00} (Best: {_highScore:00} / {_totalColonists:00}";
}
}
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// Manages game logic and flow
/// </summary>
internal class GameManager : MonoBehaviour
{
static GameManager Instance;
[SerializeField] GameObject _pauseMenu;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else Destroy(gameObject);
Time.timeScale = _pauseMenu.activeSelf ? 0 : 1;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape) || Input.GetKeyDown(KeyCode.Space))
TogglePause();
}
public void TogglePause()
{
_pauseMenu.SetActive(!_pauseMenu.activeSelf);
Time.timeScale = _pauseMenu.activeSelf ? 0 : 1;
}
public void LoadScene(int index)
=> SceneManager.LoadScene(index);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment