Skip to content

Commit 73502a7

Browse files
committed
Add all scripts
1 parent 00315ce commit 73502a7

15 files changed

+966
-0
lines changed

Assets/Scripts.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/Bunker.cs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using UnityEngine;
2+
3+
/// <summary>
4+
/// Handle bunker destruction simulations.
5+
/// </summary>
6+
[RequireComponent(typeof(SpriteRenderer))]
7+
[RequireComponent(typeof(BoxCollider2D))]
8+
public class Bunker : MonoBehaviour
9+
{
10+
/// <summary>
11+
/// The texture that will be used as an alpha mask when a projectile splats
12+
/// on the bunker.
13+
/// </summary>
14+
[Tooltip("The texture that will be used as an alpha mask when a projectile splats on the bunker.")]
15+
public Texture2D splat;
16+
17+
/// <summary>
18+
/// The sprite renderer component of the bunker.
19+
/// </summary>
20+
private SpriteRenderer _spriteRenderer;
21+
22+
/// <summary>
23+
/// The box collider component of the bunker.
24+
/// </summary>
25+
private BoxCollider2D _collider;
26+
27+
private void Awake()
28+
{
29+
_spriteRenderer = GetComponent<SpriteRenderer>();
30+
_collider = GetComponent<BoxCollider2D>();
31+
32+
// Each bunker needs a unique instance of the sprite since we will be
33+
// changing the source texture
34+
CreateSpriteInstance();
35+
}
36+
37+
private void CreateSpriteInstance()
38+
{
39+
Sprite sprite = _spriteRenderer.sprite;
40+
41+
// Create a copy of the source texture with the same properties
42+
Texture2D texture = new Texture2D(sprite.texture.width, sprite.texture.height, sprite.texture.format, false);
43+
texture.filterMode = sprite.texture.filterMode;
44+
texture.alphaIsTransparency = sprite.texture.alphaIsTransparency;
45+
texture.anisoLevel = sprite.texture.anisoLevel;
46+
texture.wrapMode = sprite.texture.wrapMode;
47+
texture.SetPixels(sprite.texture.GetPixels());
48+
texture.Apply();
49+
50+
// Create a new sprite using the cloned texture
51+
Sprite instance = Sprite.Create(texture, sprite.rect, new Vector2(0.5f, 0.5f), sprite.pixelsPerUnit);
52+
_spriteRenderer.sprite = instance;
53+
}
54+
55+
public bool CheckPoint(Vector3 hitPoint, out int px, out int py)
56+
{
57+
// Transform the point from world space to local space
58+
Vector3 localPoint = this.transform.InverseTransformPoint(hitPoint);
59+
60+
// Offset the point to the corner of the object instead of the center so
61+
// we can transform to uv coordinates
62+
localPoint.x += _collider.size.x / 2;
63+
localPoint.y += _collider.size.y / 2;
64+
65+
Texture2D texture = _spriteRenderer.sprite.texture;
66+
67+
// Transform the point from local space to uv coordinates
68+
px = (int)((localPoint.x / _collider.size.x) * texture.width);
69+
py = (int)((localPoint.y / _collider.size.y) * texture.height);
70+
71+
// Return true if the pixel is not empty (not transparent)
72+
return texture.GetPixel(px, py).a != 0.0f;
73+
}
74+
75+
public bool Splat(Vector3 hitPoint)
76+
{
77+
int px;
78+
int py;
79+
80+
// Only proceed if the point maps to a non-empty pixel
81+
if (!CheckPoint(hitPoint, out px, out py)) {
82+
return false;
83+
}
84+
85+
Texture2D texture = _spriteRenderer.sprite.texture;
86+
87+
// Offset the point by half the size of the splat texture so the splat
88+
// is centered around the hit point
89+
px -= this.splat.width / 2;
90+
py -= this.splat.height / 2;
91+
92+
int startX = px;
93+
94+
// Loop through all of the coordinates in the splat texture so we can
95+
// alpha mask the bunker texture with the splat texture
96+
for (int y = 0; y < this.splat.height; y++)
97+
{
98+
px = startX;
99+
100+
for (int x = 0; x < this.splat.width; x++)
101+
{
102+
// Multiply the alpha of the splat pixel with the alpha of the
103+
// bunker texture to make it look like parts of the bunker are
104+
// being destroyed
105+
Color pixel = texture.GetPixel(px, py);
106+
pixel.a *= this.splat.GetPixel(x, y).a;
107+
texture.SetPixel(px, py, pixel);
108+
px++;
109+
}
110+
111+
py++;
112+
}
113+
114+
// Apply any changes made to the texture
115+
texture.Apply();
116+
117+
return true;
118+
}
119+
120+
public bool CheckCollision(BoxCollider2D other, Vector3 hitPoint)
121+
{
122+
Vector2 offset = other.size / 2;
123+
124+
// Check the hit point and each edge of the colliding object to see if
125+
// it splats with the bunker for more accurate collision detection
126+
return Splat(hitPoint) ||
127+
Splat(hitPoint + (Vector3.down * offset.y)) ||
128+
Splat(hitPoint + (Vector3.up * offset.y)) ||
129+
Splat(hitPoint + (Vector3.left * offset.x)) ||
130+
Splat(hitPoint + (Vector3.right * offset.x));
131+
}
132+
133+
}

Assets/Scripts/Bunker.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/GameManager.cs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using UnityEngine;
2+
using UnityEngine.UI;
3+
4+
/// <summary>
5+
/// Manages the overall state of the game, including starting a new game and
6+
/// handling scoring and lives.
7+
/// </summary>
8+
public sealed class GameManager : MonoBehaviour
9+
{
10+
/// <summary>
11+
/// A reference to the player game object.
12+
/// </summary>
13+
[Tooltip("A reference to the player game object.")]
14+
public Player player;
15+
16+
/// <summary>
17+
/// A reference to the invaders game object.
18+
/// </summary>
19+
[Tooltip("A reference to the invaders game object.")]
20+
public Invaders invaders;
21+
22+
/// <summary>
23+
/// A reference to the MysteryShip game object.
24+
/// </summary>
25+
[Tooltip("A reference to the MysteryShip game object.")]
26+
public MysteryShip mysteryShip;
27+
28+
/// <summary>
29+
/// The UI text that displays the player's score.
30+
/// </summary>
31+
[Tooltip("The UI text that displays the player's score.")]
32+
public Text scoreText;
33+
34+
/// <summary>
35+
/// The UI text that displays the player's lives.
36+
/// </summary>
37+
[Tooltip("The UI text that displays the player's lives.")]
38+
public Text livesText;
39+
40+
/// <summary>
41+
/// The UI displayed during the game over state.
42+
/// </summary>
43+
[Tooltip("The UI displayed during the game over state.")]
44+
public GameObject gameOverUI;
45+
46+
/// <summary>
47+
/// The player's current score.
48+
/// </summary>
49+
public int score { get; private set; }
50+
51+
/// <summary>
52+
/// The player's current lives.
53+
/// </summary>
54+
public int lives { get; private set; }
55+
56+
private void Start()
57+
{
58+
// Register callbacks for game state
59+
this.player.killed += OnPlayerKilled;
60+
this.mysteryShip.killed += OnMysteryShipKilled;
61+
this.invaders.killed += OnInvaderKilled;
62+
63+
// Start a new game
64+
NewGame();
65+
}
66+
67+
private void Update()
68+
{
69+
// Start a new game once the player presses 'Return'
70+
if (this.lives == 0 && Input.GetKeyDown(KeyCode.Return)) {
71+
NewGame();
72+
}
73+
}
74+
75+
private void NewGame()
76+
{
77+
// Reset score and lives
78+
SetScore(0);
79+
SetLives(3);
80+
81+
// Reset all of the invaders
82+
this.invaders.ResetInvaders();
83+
this.invaders.gameObject.SetActive(true);
84+
85+
// Hide the game over UI
86+
this.gameOverUI.SetActive(false);
87+
88+
// Spawn the player
89+
Respawn();
90+
}
91+
92+
private void GameOver()
93+
{
94+
// Show the game over UI and hide the invaders
95+
this.gameOverUI.SetActive(true);
96+
this.invaders.gameObject.SetActive(false);
97+
}
98+
99+
private void Respawn()
100+
{
101+
// Reset the position of the player
102+
Vector3 position = this.player.transform.position;
103+
position.x = 0.0f;
104+
this.player.transform.position = position;
105+
106+
// Re-enable the player game object
107+
this.player.gameObject.SetActive(true);
108+
}
109+
110+
private void SetScore(int score)
111+
{
112+
// Set score and update UI text
113+
this.score = score;
114+
this.scoreText.text = this.score.ToString().PadLeft(4, '0');
115+
}
116+
117+
private void SetLives(int lives)
118+
{
119+
// Set lives and update UI text
120+
this.lives = lives;
121+
this.livesText.text = this.lives.ToString();
122+
}
123+
124+
private void OnPlayerKilled()
125+
{
126+
// Decrement lives by 1
127+
SetLives(this.lives - 1);
128+
129+
// Temporarily disable the player game object
130+
this.player.gameObject.SetActive(false);
131+
132+
// Respawn the player after 1 second or trigger the game over state
133+
// after running out of lives
134+
if (this.lives > 0) {
135+
Invoke(nameof(Respawn), 1.0f);
136+
} else {
137+
GameOver();
138+
}
139+
}
140+
141+
private void OnInvaderKilled(Invader invader)
142+
{
143+
// Increment score by how much the invader is worth
144+
SetScore(this.score + invader.score);
145+
}
146+
147+
private void OnMysteryShipKilled(MysteryShip mysteryShip)
148+
{
149+
// Increment score by how much the mystery ship is worth
150+
SetScore(this.score + mysteryShip.score);
151+
}
152+
153+
}

Assets/Scripts/GameManager.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/Invader.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using UnityEngine;
2+
3+
/// <summary>
4+
/// Handles animating the invader sprite and detecting when the invader dies.
5+
/// </summary>
6+
[RequireComponent(typeof(SpriteRenderer))]
7+
public class Invader : MonoBehaviour
8+
{
9+
/// <summary>
10+
/// The sprite renderer component of the invader.
11+
/// </summary>
12+
private SpriteRenderer _spriteRenderer;
13+
14+
/// <summary>
15+
/// The sprites that are animated on the invader.
16+
/// </summary>
17+
[Tooltip("The sprites that are animated on the invader.")]
18+
public Sprite[] animationSprites = new Sprite[0];
19+
20+
/// <summary>
21+
/// The amount of seconds between switching sprites to simulate animation.
22+
/// </summary>
23+
[Tooltip("The amount of seconds between switching sprites to simulate animation.")]
24+
public float animationTime = 1.0f;
25+
26+
/// <summary>
27+
/// The index of the current sprite being rendered.
28+
/// </summary>
29+
private int _animationFrame;
30+
31+
/// <summary>
32+
/// The amount of points the invader is worth when killed.
33+
/// </summary>
34+
[Tooltip("The amount of points the invader is worth when killed.")]
35+
public int score = 10;
36+
37+
/// <summary>
38+
/// The callback invoked when the invader is killed.
39+
/// </summary>
40+
public System.Action<Invader> killed;
41+
42+
private void Awake()
43+
{
44+
// Get a reference to the sprite renderer so we can animate the sprite
45+
_spriteRenderer = GetComponent<SpriteRenderer>();
46+
_spriteRenderer.sprite = this.animationSprites[0];
47+
}
48+
49+
private void Start()
50+
{
51+
// Start an animation loop to cycle between sprites
52+
InvokeRepeating(nameof(AnimateSprite), this.animationTime, this.animationTime);
53+
}
54+
55+
private void AnimateSprite()
56+
{
57+
// Move to the next animation frame
58+
_animationFrame++;
59+
60+
// Loop back to the start if the animation frame exceeds the length
61+
if (_animationFrame >= this.animationSprites.Length) {
62+
_animationFrame = 0;
63+
}
64+
65+
// Set the sprite based on the current animation frame
66+
_spriteRenderer.sprite = this.animationSprites[_animationFrame];
67+
}
68+
69+
private void OnTriggerEnter2D(Collider2D other)
70+
{
71+
// The invader dies when hit by the laser
72+
if (other.gameObject.layer == LayerMask.NameToLayer("Laser")) {
73+
this.killed?.Invoke(this);
74+
}
75+
}
76+
77+
}

0 commit comments

Comments
 (0)