Finished first chapter, added script code up to Enemy 3
This commit is contained in:
@@ -10,10 +10,14 @@ public class BoundsCheck : MonoBehaviour
|
||||
{
|
||||
[Header("Set in Inspector")]
|
||||
public float radius = 1f;
|
||||
public bool keepOnScreen = true;
|
||||
|
||||
[Header("Set Dynamically")]
|
||||
public bool isOnScreen = true;
|
||||
public float camWidth;
|
||||
public float camHeight;
|
||||
[HideInInspector]
|
||||
public bool offRight, offLeft, offUp, offDown;
|
||||
|
||||
void Awake() {
|
||||
camHeight = Camera.main.orthographicSize;
|
||||
@@ -22,23 +26,33 @@ public class BoundsCheck : MonoBehaviour
|
||||
|
||||
void LateUpdate () {
|
||||
Vector3 pos = transform.position;
|
||||
isOnScreen = true;
|
||||
offRight = offLeft = offUp = offDown = false;
|
||||
|
||||
if (pos.x > camWidth - radius) {
|
||||
pos.x = camWidth - radius;
|
||||
offRight = true;
|
||||
}
|
||||
|
||||
if (pos.x < -camWidth + radius) {
|
||||
pos.x = -camWidth + radius;
|
||||
offLeft = true;
|
||||
}
|
||||
|
||||
if (pos.y > camHeight - radius) {
|
||||
pos.y = camHeight - radius;
|
||||
offUp = true;
|
||||
}
|
||||
if (pos.y < -camHeight + radius) {
|
||||
pos.y = -camHeight + radius;
|
||||
offDown = true;
|
||||
}
|
||||
isOnScreen = !(offRight || offLeft || offUp || offDown);
|
||||
if (keepOnScreen && !isOnScreen) {
|
||||
transform.position = pos;
|
||||
isOnScreen = true;
|
||||
offRight = offLeft = offUp = offDown = false;
|
||||
}
|
||||
|
||||
transform.position = pos;
|
||||
}
|
||||
|
||||
// Draw the bounds in the Scene pane using OnDrawGizmos()
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Enemy : MonoBehaviour
|
||||
{
|
||||
[Header("Set in Inspector: Enemy")]
|
||||
public float speed = 10f; // The speed in m/s
|
||||
public float fireRate = 0.3f; // Seconds/shot (Unused)
|
||||
public float health = 10;
|
||||
public int score = 100; // Points earned for destroying this
|
||||
protected BoundsCheck bndCheck;
|
||||
|
||||
void Awake() {
|
||||
bndCheck = GetComponent<BoundsCheck>();
|
||||
}
|
||||
|
||||
// This is a Property: A method that acts like a field
|
||||
public Vector3 pos {
|
||||
get {
|
||||
return( this.transform.position );
|
||||
}
|
||||
set {
|
||||
this.transform.position = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Move() {
|
||||
Vector3 tempPos = pos;
|
||||
tempPos.y -= speed * Time.deltaTime;
|
||||
pos = tempPos;
|
||||
}
|
||||
|
||||
void OnCollisionEnter(Collision coll) {
|
||||
GameObject otherGO = coll.gameObject;
|
||||
if (otherGO.tag == "ProjectileHero") {
|
||||
Destroy(otherGO); // Destroy the Projectile
|
||||
Destroy(gameObject); // Destroy this Enemy GameObject
|
||||
} else {
|
||||
print("Enemy hit by non-ProjectileHero: " + otherGO.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
Move();
|
||||
if (bndCheck != null && bndCheck.offDown) {
|
||||
// We're off the bottom, so destroy this GameObject
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf379ee257cd884c58f85b182bc79ba4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
// Enemy_1 extends the Enemy class
|
||||
public class Enemy_1 : Enemy
|
||||
{
|
||||
[Header("Set in Inspector: Enemy_1")]
|
||||
// # seconds for a full sine wave
|
||||
public float waveFrequency = 2;
|
||||
// sine wave width in meters
|
||||
public float waveWidth = 4;
|
||||
public float waveRotY = 45;
|
||||
private float x0; // The initial x value of pos
|
||||
private float birthTime;
|
||||
|
||||
// Start works well because it's not used by the Enemy superclass
|
||||
void Start() {
|
||||
// Set x0 to the initial x position of Enemy_1
|
||||
x0 = pos.x;
|
||||
birthTime = Time.time;
|
||||
}
|
||||
|
||||
// Override the Move function on Enemy
|
||||
public override void Move() {
|
||||
// Because pos is a property, you can't directly set pos.x
|
||||
// so get the pos as an editable Vector3
|
||||
Vector3 tempPos = pos;
|
||||
// theta adjusts based on time
|
||||
float age = Time.time - birthTime;
|
||||
float theta = Mathf.PI * 2 * age / waveFrequency;
|
||||
float sin = Mathf.Sin(theta);
|
||||
tempPos.x = x0 + waveWidth * sin;
|
||||
pos = tempPos;
|
||||
// rotate a bit about y
|
||||
Vector3 rot = new Vector3(0, sin*waveRotY, 0);
|
||||
this.transform.rotation = Quaternion.Euler(rot);
|
||||
// base.Move() still handles the movement down in y
|
||||
base.Move();
|
||||
// print(bndCheck.isOnScreen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67239b4e04039f3d28db4ea469fb531c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Enemy_2 : Enemy
|
||||
{
|
||||
[Header("Set in Inspector: Enemy_2")]
|
||||
// Determines how much the Sine wave will affect movement
|
||||
public float sinEccentricity = 0.6f;
|
||||
public float lifeTime = 10;
|
||||
[Header("Set Dynamically: Enemy_2")]
|
||||
// Enemy_2 uses a Sin wave to modify a 2-point linear interpolation
|
||||
public Vector3 p0;
|
||||
public Vector3 p1;
|
||||
public float birthTime;
|
||||
void Start() {
|
||||
// Pick any point on the left side of the screen
|
||||
p0 = Vector3.zero;
|
||||
p0.x = -bndCheck.camWidth - bndCheck.radius;
|
||||
p0.y = Random.Range(-bndCheck.camHeight, bndCheck.camHeight);
|
||||
// Pick any point on the right side of the screen
|
||||
p1 = Vector3.zero;
|
||||
p1.x = bndCheck.camWidth + bndCheck.radius;
|
||||
p1.y = Random.Range(-bndCheck.camHeight, bndCheck.camHeight);
|
||||
// Possibly swap sides
|
||||
if (Random.value > 0.5f) {
|
||||
// Setting the .x of each point to its negative will move it to
|
||||
// the other side of the screen
|
||||
p0.x *= -1;
|
||||
p1.x *= -1;
|
||||
}
|
||||
// Set the birthTime to the current time
|
||||
birthTime = Time.time;
|
||||
}
|
||||
public override void Move() {
|
||||
// Bezier curves work based on a u value between 0 & 1
|
||||
float u = (Time.time - birthTime) / lifeTime;
|
||||
// If u>1, then it has been longer than lifeTime since birthTime
|
||||
if (u > 1) {
|
||||
// This Enemy_2 has finished its life
|
||||
Destroy(this.gameObject);
|
||||
return;
|
||||
}
|
||||
// Adjust u by adding a U Curve based on a Sine wave
|
||||
u = u + sinEccentricity*(Mathf.Sin(u*Mathf.PI*2));
|
||||
pos = (1-u)*p0 + u*p1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8424fdbcb6a86d74f860fe068d094d3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Enemy_3 : Enemy
|
||||
{
|
||||
// Enemy_3 will move following a Bezier curve, which is a linear
|
||||
// interpolation between more than two points.
|
||||
[Header("Set in Inspector: Enemy_3")]
|
||||
public float lifeTime = 5;
|
||||
[Header("Set Dynamically: Enemy_3")]
|
||||
public Vector3[] points;
|
||||
public float birthTime;
|
||||
// Again, Start works well because its not used by the Enemy superclass
|
||||
void Start() {
|
||||
points = new Vector3[3]; // Initialize points
|
||||
// The start position has already been set by Main.SpawnEnemy()
|
||||
points[0] = pos;
|
||||
// Set xMin and xMax that same way that Main.SpawnEnemy() does
|
||||
float xMin = -bndCheck.camWidth + bndCheck.radius;
|
||||
float xMax = bndCheck.camWidth - bndCheck.radius;
|
||||
Vector3 v = Vector3.zero;
|
||||
// Pick a random middle position in the bottom half of the screen;
|
||||
v.x = Random.Range(xMin, xMax);
|
||||
v.y = -bndCheck.camHeight * Random.Range(2.75f, 2);
|
||||
points[1] = v;
|
||||
// Pick a random final position above the top of the screen
|
||||
v = Vector3.zero;
|
||||
v.y = pos.y;
|
||||
v.x = Random.Range(xMin, xMax);
|
||||
points[2] = v;
|
||||
// Set the birthTime to the current time
|
||||
birthTime = Time.time;
|
||||
}
|
||||
public override void Move() {
|
||||
// Bezier curves work based on a u value between 0 & 1
|
||||
float u = (Time.time - birthTime) / lifeTime;
|
||||
if (u > 1) {
|
||||
// This Enemy_3 has finished its life
|
||||
Destroy(this.gameObject);
|
||||
return;
|
||||
}
|
||||
// Interpolate the three Bezier cruve points
|
||||
Vector3 p01, p12;
|
||||
u = u - 0.2f*Mathf.Sin(u * Mathf.PI * 2);
|
||||
p01 = (1-u)*points[0] + u*points[1];
|
||||
p12 = (1-u)*points[1] + u*points[2];
|
||||
pos = (1-u)*p01 + u*p12;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 949d50caf3a9277bd80ee88fcc10bf9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Enemy_4 : Enemy
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 890512b9c88f600408f25130d273807b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -11,9 +11,14 @@ public class Hero : MonoBehaviour
|
||||
public float speed = 30;
|
||||
public float rollMult = -45;
|
||||
public float pitchMult = 30;
|
||||
public float gameRestartDelay = 2f;
|
||||
public GameObject projectilePrefab;
|
||||
public float projectileSpeed = 40;
|
||||
|
||||
[Header("Set Dynamically")]
|
||||
public float shieldLevel = 1;
|
||||
public float _shieldLevel = 1;
|
||||
// This variables holds a reference to the last triggering GameObject
|
||||
private GameObject lastTriggerGo = null;
|
||||
|
||||
void Awake() {
|
||||
if (S == null) {
|
||||
@@ -44,5 +49,47 @@ public class Hero : MonoBehaviour
|
||||
|
||||
// Rotate the ship to make it feel more dynamic
|
||||
transform.rotation = Quaternion.Euler(yAxis*pitchMult,xAxis*rollMult,0);
|
||||
// Allow the ship to fire
|
||||
if (Input.GetKeyDown(KeyCode.Space)) {
|
||||
TempFire();
|
||||
}
|
||||
}
|
||||
|
||||
void TempFire() {
|
||||
GameObject projGO = Instantiate<GameObject>(projectilePrefab);
|
||||
projGO.transform.position = transform.position;
|
||||
Rigidbody rigidB = projGO.GetComponent<Rigidbody>();
|
||||
rigidB.velocity = Vector3.up * projectileSpeed;
|
||||
}
|
||||
|
||||
void OnTriggerEnter(Collider other) {
|
||||
Transform rootT = other.gameObject.transform.root;
|
||||
GameObject go = rootT.gameObject;
|
||||
//print("Triggered: " + go.name);
|
||||
// Make sure it's not the same triggering go as last time
|
||||
if (go == lastTriggerGo) {
|
||||
return;
|
||||
}
|
||||
lastTriggerGo = go;
|
||||
if (go.tag == "Enemy") {
|
||||
shieldLevel--;
|
||||
Destroy(go);
|
||||
} else {
|
||||
print("Triggered by non-Enemy: " + go.name);
|
||||
}
|
||||
}
|
||||
public float shieldLevel {
|
||||
get {
|
||||
return(_shieldLevel);
|
||||
}
|
||||
set {
|
||||
_shieldLevel = Mathf.Min(value, 4);
|
||||
// If the shield is going to be set to less than zero
|
||||
if (value < 0) {
|
||||
Destroy(this.gameObject);
|
||||
// Tell Main.S to restart the game after a delay
|
||||
Main.S.DelayedRestart(gameRestartDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class Main : MonoBehaviour
|
||||
{
|
||||
static public Main S;
|
||||
[Header("Set in Inspector")]
|
||||
public GameObject[] prefabEnemies;
|
||||
public float enemySpawnPerSecond = 0.5f;
|
||||
public float enemyDefaultPadding = 1.5f;
|
||||
private BoundsCheck bndCheck;
|
||||
|
||||
void Awake() {
|
||||
S = this;
|
||||
// Set bndCheck to reference the BoundsCheck component of this GameObject
|
||||
bndCheck = GetComponent<BoundsCheck>();
|
||||
// Invoke SpawnEnemy() once (in 2 seconds, based on default values)
|
||||
Invoke("SpawnEnemy", 1f/enemySpawnPerSecond);
|
||||
}
|
||||
|
||||
public void SpawnEnemy() {
|
||||
// Pick a random Enemy prefab to instantiate
|
||||
int ndx = Random.Range(0, prefabEnemies.Length);
|
||||
GameObject go = Instantiate<GameObject>(prefabEnemies[ndx]);
|
||||
// Position the Enemy above the screen with a random x position
|
||||
float enemyPadding = enemyDefaultPadding;
|
||||
if (go.GetComponent<BoundsCheck>() != null) {
|
||||
enemyPadding = Mathf.Abs(go.GetComponent<BoundsCheck>().radius);
|
||||
}
|
||||
// Set the initial position for the spawned Enemy
|
||||
Vector3 pos = Vector3.zero;
|
||||
float xMin = -bndCheck.camWidth + enemyPadding;
|
||||
float xMax = bndCheck.camWidth - enemyPadding;
|
||||
pos.x = Random.Range(xMin, xMax);
|
||||
pos.y = bndCheck.camHeight + enemyPadding;
|
||||
go.transform.position = pos;
|
||||
// Invoke SpawnEnemy() again
|
||||
Invoke("SpawnEnemy", 1f/enemySpawnPerSecond);
|
||||
}
|
||||
|
||||
public void DelayedRestart(float delay) {
|
||||
// Invoke the Restart() method in delay seconds
|
||||
Invoke("Restart", delay);
|
||||
}
|
||||
public void Restart() {
|
||||
// Reload _Scene_0 to restart the game
|
||||
SceneManager.LoadScene("_Scene_0");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e92a40d35c2c59b9c955492aa4ff067a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Projectile : MonoBehaviour
|
||||
{
|
||||
private BoundsCheck bndCheck;
|
||||
|
||||
void Awake() {
|
||||
bndCheck = GetComponent<BoundsCheck>();
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
if (bndCheck.offUp) {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74ad9c373a5d2b5e69bdb49a1f50e209
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user