The main controller of the ship, 2D movement, only horizontal
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
public class ShipController : MonoBehaviour
{
[Header("Movement")]
public float speed = 5f;
private Rigidbody2D rigidBody2D;
Vector2 movement;
[Header("Fire")]
public GameObject laser;
public float fireRate = 0.8f;
public float laserSpeed = 600f;
private float nextFire = 0f;
[Header("Explosion")]
public GameObject explosion;
[Header("Other")]
public float immortalityDuration = 1f;
void Start()
{
StartCoroutine(TemporalImmortalityCoroutine());
rigidBody2D = GetComponent<Rigidbody2D>();
IObservable<Unit> update = this.UpdateAsObservable();
update
.Do(_ => HandleMovement())
.Where(_ => CanShoot())
.Subscribe(_ => Fire());
}
private void HandleMovement()
{
movement.x = Input.GetAxisRaw("Horizontal");
rigidBody2D.MovePosition(rigidBody2D.position + movement * speed * Time.fixedDeltaTime);
}
public void Explode()
{
GameObject explosionInstance = Instantiate(explosion, transform.position, transform.rotation);
Destroy(explosionInstance, 0.4f);
Destroy(gameObject);
GameController controller =
GameObject.FindGameObjectWithTag("GameController")
.GetComponent("GameController")
as GameController;
controller.SubstractLive();
}
private void Fire()
{
GameObject firedLaser = Instantiate(
laser,
transform.position,
transform.rotation
);
Rigidbody2D firedLaserRigidBody2D = firedLaser.GetComponent<Rigidbody2D>();
firedLaserRigidBody2D.AddForce(Vector3.up * laserSpeed);
Physics2D.IgnoreCollision(firedLaser.GetComponent<Collider2D>(), GetComponent<Collider2D>());
nextFire = Time.time + fireRate;
}
private bool CanShoot() => Input.GetButton("Fire1") && Time.time > nextFire;
IEnumerator TemporalImmortalityCoroutine()
{
float startTime = Time.time;
GetComponent<Collider2D>().enabled = false;
GetComponent<SpriteRenderer>().color = new Color(1f, 1f, 1f, 0.5f);
yield return new WaitForSeconds(immortalityDuration);
GetComponent<Collider2D>().enabled = true;
GetComponent<SpriteRenderer>().color = new Color(1f, 1f, 1f, 1f);
}
}
The code for the laser proyectile
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
public class Laser : MonoBehaviour
{
void Start()
{
IObservable<Collision2D> collisionEnterObservable = this.OnCollisionEnter2DAsObservable();
collisionEnterObservable
.Do(Impact)
.Where(IsUfo)
.Subscribe(DestroyUfo);
collisionEnterObservable
.Where(IsPlayer)
.Subscribe(DestroyPlayer);
}
private void Impact(Collision2D collision)
{
Destroy(gameObject);
}
private void DestroyUfo(Collision2D collision)
{
Ufo ufo = collision.gameObject.GetComponent<Ufo>();
ufo.Explode();
}
private void DestroyPlayer(Collision2D collision)
{
ShipController player = collision.gameObject.GetComponent<ShipController>();
player.Explode();
}
private bool IsUfo(Collision2D collision) => collision.gameObject.tag == "Ufo";
private bool IsPlayer(Collision2D collision) => collision.gameObject.tag == "Player";
}
The Ufo enemy script
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
public class Ufo : MonoBehaviour
{
public GameObject explosion;
public GameObject laser;
public float laserSpeed = 200f;
private void Start()
{
Physics2D.queriesStartInColliders = false;
Observable
.Interval(TimeSpan.FromSeconds(UnityEngine.Random.Range(1f, 2f)))
.Where(_ => CanShoot())
.Subscribe(_ => Fire())
.AddTo(this);
}
public void Explode()
{
GameObject explosionInstance = Instantiate(explosion, transform.position, transform.rotation);
Destroy(explosionInstance, 0.4f);
Destroy(gameObject);
GameController controller =
GameObject.FindGameObjectWithTag("GameController")
.GetComponent("GameController")
as GameController;
controller.checkUfosLeft();
}
private void Fire()
{
GameObject firedLaser = Instantiate(
laser,
transform.position,
Quaternion.Euler(0, 0, 180)
);
Physics2D.IgnoreCollision(firedLaser.GetComponent<Collider2D>(), GetComponent<Collider2D>());
Rigidbody2D firedLaserRigidBody2D = firedLaser.GetComponent<Rigidbody2D>();
firedLaserRigidBody2D.AddForce(Vector2.down * laserSpeed);
}
private bool CanShoot()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down);
return hit.collider.tag != "Ufo" && UnityEngine.Random.Range(1, 4) == 2;
}
}
The movement of the ufo group, must be attached to a gameobject that is parent of the group of ufos (see video tutorial)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
public class UfoMovement : MonoBehaviour
{
private bool isMovingRight = true;
public float baseSpeed = 0.4f;
void Start()
{
IObservable<Unit> update = this.UpdateAsObservable();
IObservable<Collider2D> triggerEnterObservable = this.OnTriggerEnter2DAsObservable();
update.Subscribe(_ => Move());
triggerEnterObservable
.Where(IsBorder)
.Subscribe(_ => ChangeDirection());
triggerEnterObservable
.Where(IsPlayer)
.Subscribe(_ => GameOver());
}
private void Move()
{
float movement = baseSpeed * Time.fixedDeltaTime * 0.1f * Time.timeScale;
transform.Translate(isMovingRight ? movement : -movement, 0, 0);
}
private void ChangeDirection()
{
isMovingRight = !isMovingRight;
baseSpeed += 0.3f;
transform.Translate(0, -0.4f, 0);
}
private void GameOver()
{
GameController controller =
GameObject.FindGameObjectWithTag("GameController")
.GetComponent("GameController")
as GameController;
controller.GameOver();
}
private bool IsBorder(Collider2D collider) => collider.tag == "MainCamera";
private bool IsPlayer(Collider2D collider) => collider.tag == "Player";
}
The main game controller
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UniRx;
public class GameController : MonoBehaviour
{
[Header("Game Settings")]
[SerializeField] private int lives = 3;
public GameObject playerPrefab;
private ReactiveProperty<int> reactiveLives;
public GameObject ufosContainer;
private ReactiveProperty<int> ufosLeft;
[Header("UI")]
public GameObject livesUI;
public GameObject gameOverUI;
public GameObject winUI;
private Text livesLeftText;
void Start()
{
reactiveLives = new ReactiveProperty<int>(lives);
livesLeftText = livesUI.GetComponent<Text>();
livesLeftText.text = $"Lives: {lives}";
ufosLeft = new ReactiveProperty<int>(1);
BindRestartButtons();
ObserveLives();
ObserveUfos();
}
private void BindRestartButtons()
{
gameOverUI.transform.Find("RestartButton")
.GetComponent<Button>()
.onClick.AddListener(RestartGame);
winUI.transform.Find("RestartButton")
.GetComponent<Button>()
.onClick.AddListener(RestartGame);
}
private void ObserveUfos()
{
ufosLeft
.Where(ufos => ufos == 0)
.Take(1)
.Subscribe(_ => WinGame())
.AddTo(this);
}
private void ObserveLives()
{
reactiveLives
.Where(livesLeft => livesLeft == 0)
.Take(1)
.Subscribe(_ => GameOver())
.AddTo(this);
}
public void GameOver()
{
gameOverUI.SetActive(true);
Time.timeScale = 0;
}
public void WinGame()
{
winUI.SetActive(true);
Time.timeScale = 0;
}
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
Time.timeScale = 1;
}
private void SpawnPlayer()
{
Instantiate(playerPrefab, new Vector3(0, -4 ,0), Quaternion.Euler(0, 0, 0));
}
public void checkUfosLeft()
{
Collider2D ufosContainerCollider = ufosContainer.GetComponent<Collider2D>();
ContactFilter2D filter = new ContactFilter2D();
filter.SetLayerMask(LayerMask.GetMask("Ufos"));
ufosLeft.Value = Physics2D.OverlapCollider(ufosContainerCollider, filter, new List<Collider2D>());
}
public void SubstractLive()
{
reactiveLives.Value -= 1;
livesLeftText.text = $"Lives: {reactiveLives.Value}";
if (reactiveLives.Value != 0) {
SpawnPlayer();
}
}
}