[Invaders] Upgrade Invaders to Unity 2020.3.13 LTS + a bunch of fixes and refactoring passes, read below
- Game - rename all our alien prefabs to have a more generic name, the same principle was applied to our codebase, renamed variables/fields/classes to something more generic - Enemies: Rename our main enemy class to EnemyAgent + minor clean-ups + implement a grace shoot timer period - Rename AllienBullet to Enemy Bullet - InvadersGame: Some big refactories here, the UpdateEnemies function not outputs a set of flags (bitmask) rather than having separated booleans to keep track off - InvadersGame: Fix an edge case of the game loop where if the enemies would reach the bottom they would never respawn, now when they do reach that bottom boundary it will be game over - InvadersGame: Introduce additional game over reasons - LobbyControl: Introduce a minimum player count variable that could be tweaked in inspector so that the users can start playing in editor with just the host in the lobby - PlayerControl: Unified the NotifyGameOver function with the InvadersGame one + added different texts for all the possible game over reasons to be displayed
This commit is contained in:
Родитель
61fb994c24
Коммит
5b95753416
|
@ -44,10 +44,10 @@ MonoBehaviour:
|
|||
m_Script: {fileID: 11500000, guid: d643b609c6a71c54984c1ad454faa28b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
alien1Prefab: {fileID: 100000, guid: 43ae6ad1bdadd0747811bb7a00740c2c, type: 3}
|
||||
alien2Prefab: {fileID: 100000, guid: 38a8441efb2d6d245adbf08f08705374, type: 3}
|
||||
alien3Prefab: {fileID: 100000, guid: aa50d36274d0dfc4faff2c69820b3711, type: 3}
|
||||
saucerPrefab: {fileID: 100000, guid: e811e4fb062e31444aaf36dbec5de77c, type: 3}
|
||||
enemy1Prefab: {fileID: 100000, guid: 43ae6ad1bdadd0747811bb7a00740c2c, type: 3}
|
||||
enemy2Prefab: {fileID: 100000, guid: 38a8441efb2d6d245adbf08f08705374, type: 3}
|
||||
enemy3Prefab: {fileID: 100000, guid: aa50d36274d0dfc4faff2c69820b3711, type: 3}
|
||||
superEnemyPrefab: {fileID: 100000, guid: e811e4fb062e31444aaf36dbec5de77c, type: 3}
|
||||
shieldPrefab: {fileID: 100000, guid: b990a6e6dcf54b646a7397028fed6045, type: 3}
|
||||
gameTimerText: {fileID: 0}
|
||||
scoreText: {fileID: 0}
|
||||
|
@ -57,7 +57,7 @@ MonoBehaviour:
|
|||
m_DelayedStartTime: 5
|
||||
m_TickPeriodic:
|
||||
m_InternalValue: 0.2
|
||||
m_AlienDirection:
|
||||
m_EnemyMovingDirection:
|
||||
m_InternalValue: 0.3
|
||||
m_RandomThresholdForSaucerCreation: 0.92
|
||||
--- !u!114 &4266113290039088509
|
||||
|
|
|
@ -100,7 +100,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
owner: {fileID: 0}
|
||||
m_TravelSpeed: 6
|
||||
m_TravelSpeed: 7.25
|
||||
--- !u!61 &6100000
|
||||
BoxCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
@ -16,7 +16,7 @@ GameObject:
|
|||
- component: {fileID: 4223722966607089368}
|
||||
- component: {fileID: 2037077820506562951}
|
||||
m_Layer: 0
|
||||
m_Name: alien1
|
||||
m_Name: enemy1
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
|
@ -16,7 +16,7 @@ GameObject:
|
|||
- component: {fileID: 8401436849663568366}
|
||||
- component: {fileID: 1361474663871475650}
|
||||
m_Layer: 0
|
||||
m_Name: alien2
|
||||
m_Name: enemy2
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
|
@ -16,7 +16,7 @@ GameObject:
|
|||
- component: {fileID: 8349640660830732066}
|
||||
- component: {fileID: 7651551376209856180}
|
||||
m_Layer: 0
|
||||
m_Name: alien3
|
||||
m_Name: enemy3
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
|
@ -16,7 +16,7 @@ GameObject:
|
|||
- component: {fileID: 2336033219118315817}
|
||||
- component: {fileID: 5836440591138289839}
|
||||
m_Layer: 0
|
||||
m_Name: alienbullet
|
||||
m_Name: enemyBullet
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
|
@ -17,7 +17,7 @@ GameObject:
|
|||
- component: {fileID: 8984612552865464625}
|
||||
- component: {fileID: 3955316467729786863}
|
||||
m_Layer: 0
|
||||
m_Name: saucerr
|
||||
m_Name: superEnemy
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
|
@ -253,7 +253,7 @@ RectTransform:
|
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 1}
|
||||
m_AnchorMax: {x: 0.5, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: -146.79999}
|
||||
m_AnchoredPosition: {x: 0, y: -75.218}
|
||||
m_SizeDelta: {x: 90, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1 &16114619
|
||||
|
@ -557,7 +557,7 @@ MonoBehaviour:
|
|||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4280690380
|
||||
m_fontColor: {r: 0.8018868, g: 0.14751692, b: 0.14751692, a: 1}
|
||||
m_fontColor: {r: 0.990566, g: 0, b: 0.13591357, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
|
@ -809,7 +809,7 @@ RectTransform:
|
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 1, y: 1}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: -361.4607, y: -142}
|
||||
m_AnchoredPosition: {x: -200, y: -52.172}
|
||||
m_SizeDelta: {x: 135.624, y: 46.0926}
|
||||
m_Pivot: {x: 1, y: 1}
|
||||
--- !u!114 &740254072
|
||||
|
@ -1332,7 +1332,7 @@ RectTransform:
|
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 1}
|
||||
m_AnchorMax: {x: 0.5, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: -168.6}
|
||||
m_AnchoredPosition: {x: 0, y: -109}
|
||||
m_SizeDelta: {x: 72.0173, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 1}
|
||||
--- !u!114 &1087760897
|
||||
|
@ -1472,7 +1472,7 @@ RectTransform:
|
|||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 400, y: -123.363464}
|
||||
m_AnchoredPosition: {x: 200, y: -56}
|
||||
m_SizeDelta: {x: 186.1548, y: 38.4365}
|
||||
m_Pivot: {x: 0, y: 1}
|
||||
--- !u!114 &1118830665
|
||||
|
@ -1846,11 +1846,6 @@ PrefabInstance:
|
|||
propertyPath: saucerSpawnPoint
|
||||
value:
|
||||
objectReference: {fileID: 1031245994}
|
||||
- target: {fileID: 4266113290039088507, guid: 67eb58ec33df0475ca152786107f9037,
|
||||
type: 3}
|
||||
propertyPath: m_DelayedStartTime
|
||||
value: 10
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4266113290039088508, guid: 67eb58ec33df0475ca152786107f9037,
|
||||
type: 3}
|
||||
propertyPath: m_Name
|
||||
|
|
|
@ -532,6 +532,7 @@ MonoBehaviour:
|
|||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_InGameSceneName: InGame
|
||||
m_MinimumPlayerCount: 2
|
||||
LobbyText: {fileID: 888252281}
|
||||
--- !u!114 &698002441
|
||||
MonoBehaviour:
|
||||
|
|
|
@ -4,58 +4,26 @@ using UnityEngine;
|
|||
using UnityEngine.Assertions;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
public class AlienInvader : NetworkBehaviour
|
||||
public class EnemyAgent : NetworkBehaviour
|
||||
{
|
||||
private const float k_ShootingRandomThreshold = 0.996f;
|
||||
private const float k_ShootTimer = 0.75f;
|
||||
[Header("Alien Settings")]
|
||||
private const float k_ShootTimer = 1.25f;
|
||||
[Header("Enemy Settings")]
|
||||
public int score = 50;
|
||||
public GameObject bulletPrefab;
|
||||
public float GraceShootingPeriod = 1.0f; // A period of time in which the enemy will not shoot at the start
|
||||
|
||||
public bool canShoot { get; set; }
|
||||
public float column { get; private set; }
|
||||
public float row { get; private set; }
|
||||
|
||||
private float m_ShootTimer = 0.0f;
|
||||
private float m_FirstShootTimeAfterSpawn = 0.0f;
|
||||
|
||||
private void Update()
|
||||
public void Awake()
|
||||
{
|
||||
bool bCanShootThisFrame = false;
|
||||
if (IsServer && canShoot)
|
||||
if (Random.Range(0, 1.0f) > k_ShootingRandomThreshold)
|
||||
bCanShootThisFrame = true;
|
||||
|
||||
if (m_ShootTimer > 0)
|
||||
m_ShootTimer -= Time.deltaTime;
|
||||
else
|
||||
{
|
||||
if (!bCanShootThisFrame) return;
|
||||
m_ShootTimer = k_ShootTimer;
|
||||
SpawnBullet();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnBullet()
|
||||
{
|
||||
var myBullet = Instantiate(bulletPrefab, transform.position - Vector3.up, Quaternion.identity);
|
||||
myBullet.GetComponent<NetworkObject>().Spawn();
|
||||
}
|
||||
|
||||
protected void OnDestroy()
|
||||
{
|
||||
if (!InvadersGame.Singleton) return;
|
||||
|
||||
if (IsServer) InvadersGame.Singleton.UnregisterSpawnableObject(InvadersObjectType.Alien, gameObject);
|
||||
InvadersGame.Singleton.isGameOver.OnValueChanged -= OnGameOver;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D collider)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
var hitShield = collider.gameObject.GetComponent<Shield>();
|
||||
if (hitShield != null) Destroy(hitShield.gameObject);
|
||||
canShoot = false;
|
||||
m_FirstShootTimeAfterSpawn = Single.PositiveInfinity;
|
||||
}
|
||||
|
||||
public override void NetworkStart()
|
||||
|
@ -68,12 +36,61 @@ public class AlienInvader : NetworkBehaviour
|
|||
if (score == 100)
|
||||
return;
|
||||
|
||||
m_FirstShootTimeAfterSpawn =
|
||||
Time.time + Random.Range(GraceShootingPeriod - 0.1f, GraceShootingPeriod + 0.75f);
|
||||
|
||||
Assert.IsTrue(InvadersGame.Singleton);
|
||||
InvadersGame.Singleton.RegisterSpawnableObject(InvadersObjectType.Alien, gameObject);
|
||||
InvadersGame.Singleton.RegisterSpawnableObject(InvadersObjectType.Enemy, gameObject);
|
||||
InvadersGame.Singleton.isGameOver.OnValueChanged += OnGameOver;
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnDestroy()
|
||||
{
|
||||
if (!InvadersGame.Singleton) return;
|
||||
|
||||
if (IsServer) InvadersGame.Singleton.UnregisterSpawnableObject(InvadersObjectType.Enemy, gameObject);
|
||||
InvadersGame.Singleton.isGameOver.OnValueChanged -= OnGameOver;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Time.time <= m_FirstShootTimeAfterSpawn)
|
||||
{
|
||||
// Wait for the grace shooting period to pass
|
||||
return;
|
||||
}
|
||||
|
||||
bool bCanShootThisFrame = false;
|
||||
if (IsServer && canShoot)
|
||||
if (Random.Range(0, 1.0f) > k_ShootingRandomThreshold)
|
||||
bCanShootThisFrame = true;
|
||||
|
||||
if (m_ShootTimer > 0)
|
||||
m_ShootTimer -= Time.deltaTime;
|
||||
else
|
||||
{
|
||||
if (!bCanShootThisFrame) return;
|
||||
m_ShootTimer = Random.Range(k_ShootTimer - 0.05f, k_ShootTimer + 0.25f);
|
||||
SpawnBullet();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnBullet()
|
||||
{
|
||||
var myBullet = Instantiate(bulletPrefab, transform.position - Vector3.up, Quaternion.identity);
|
||||
myBullet.GetComponent<NetworkObject>().Spawn();
|
||||
}
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D collider)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
var hitShield = collider.gameObject.GetComponent<Shield>();
|
||||
if (hitShield != null) Destroy(hitShield.gameObject);
|
||||
}
|
||||
|
||||
private void OnGameOver(bool oldValue, bool newValue)
|
||||
{
|
||||
// Is there anything we need to add in here?
|
|
@ -3,7 +3,7 @@ using MLAPI;
|
|||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
public class AlienBullet : MonoBehaviour
|
||||
public class EnemyBullet : MonoBehaviour
|
||||
{
|
||||
private const float k_YBoundary = -4.0f;
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using MLAPI;
|
||||
using MLAPI.Connection;
|
||||
using MLAPI.Messaging;
|
||||
using MLAPI.NetworkVariable;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
@ -10,20 +11,41 @@ using Random = UnityEngine.Random;
|
|||
|
||||
public enum InvadersObjectType
|
||||
{
|
||||
Alien = 1,
|
||||
Enemy = 1,
|
||||
Shield,
|
||||
Max
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum UpdateEnemiesResultFlags : byte
|
||||
{
|
||||
None = 0x0000,
|
||||
FoundEnemy = 0x0001, // Found at least one eligible enemy to continue, without creating a new set
|
||||
ReachedHorizontalBoundary = 0x0002, // If at least one of the enemies reached either left or right boundary
|
||||
ReachedBottom = 0x004, // If at least one of the enemies reached the bottom boundary the game is over
|
||||
Max
|
||||
}
|
||||
|
||||
public enum GameOverReason : byte
|
||||
{
|
||||
None = 0,
|
||||
EnemiesReachedBottom = 1,
|
||||
Death = 2,
|
||||
Max,
|
||||
}
|
||||
|
||||
public class InvadersGame : NetworkBehaviour
|
||||
{
|
||||
// The vertical offset we apply to each Alien transform once they touch an edge
|
||||
private const float k_AlienVerticalMovementOffset = -0.8f;
|
||||
// The vertical offset we apply to each Enemy transform once they touch an edge
|
||||
private const float k_EnemyVerticalMovementOffset = -0.8f;
|
||||
private const float k_LeftOrRightBoundaryOffset = 10.0f;
|
||||
private const float k_BottomBoundaryOffset = 1.25f;
|
||||
|
||||
[Header("Prefab settings")]
|
||||
public GameObject alien1Prefab;
|
||||
public GameObject alien2Prefab;
|
||||
public GameObject alien3Prefab;
|
||||
public GameObject saucerPrefab;
|
||||
public GameObject enemy1Prefab;
|
||||
public GameObject enemy2Prefab;
|
||||
public GameObject enemy3Prefab;
|
||||
public GameObject superEnemyPrefab;
|
||||
public GameObject shieldPrefab;
|
||||
|
||||
[Header("UI Settings")]
|
||||
|
@ -43,12 +65,12 @@ public class InvadersGame : NetworkBehaviour
|
|||
private NetworkVariableFloat m_TickPeriodic = new NetworkVariableFloat(0.2f);
|
||||
|
||||
[SerializeField]
|
||||
private NetworkVariableFloat m_AlienDirection = new NetworkVariableFloat(0.3f);
|
||||
private NetworkVariableFloat m_EnemyMovingDirection = new NetworkVariableFloat(0.3f);
|
||||
|
||||
[SerializeField]
|
||||
private float m_RandomThresholdForSaucerCreation = 0.92f;
|
||||
|
||||
private List<AlienInvader> m_Aliens = new List<AlienInvader>();
|
||||
private List<EnemyAgent> m_Enemies = new List<EnemyAgent>();
|
||||
|
||||
//These help to simplify checking server vs client
|
||||
//[NSS]: This would also be a great place to add a state machine and use networked vars for this
|
||||
|
@ -127,7 +149,7 @@ public class InvadersGame : NetworkBehaviour
|
|||
{
|
||||
if (IsServer)
|
||||
{
|
||||
m_Aliens.Clear();
|
||||
m_Enemies.Clear();
|
||||
m_Shields.Clear();
|
||||
}
|
||||
}
|
||||
|
@ -268,90 +290,105 @@ public class InvadersGame : NetworkBehaviour
|
|||
|
||||
/// <summary>
|
||||
/// OnGameStarted
|
||||
/// Only invoked by the server, this hides the timer text and initializes the aliens and level
|
||||
/// Only invoked by the server, this hides the timer text and initializes the enemies and level
|
||||
/// </summary>
|
||||
private void OnGameStarted()
|
||||
{
|
||||
gameTimerText.gameObject.SetActive(false);
|
||||
CreateAliens();
|
||||
CreateEnemies();
|
||||
CreateShields();
|
||||
CreateSaucer();
|
||||
CreateSuperEnemy();
|
||||
}
|
||||
|
||||
private void UpdateEnemies()
|
||||
{
|
||||
// update aliens
|
||||
// Update enemies
|
||||
if (Time.time >= m_NextTick)
|
||||
{
|
||||
m_NextTick = Time.time + m_TickPeriodic.Value;
|
||||
|
||||
var foundEdge = false;
|
||||
var foundEligibleAlienEnemy = false;
|
||||
UpdateShootingEnemies(ref foundEligibleAlienEnemy, ref foundEdge);
|
||||
UpdateEnemiesResultFlags enemiesResultFlags = UpdateEnemiesResultFlags.None;
|
||||
UpdateShootingEnemies(ref enemiesResultFlags);
|
||||
|
||||
if (!foundEligibleAlienEnemy)
|
||||
if((enemiesResultFlags & UpdateEnemiesResultFlags.ReachedBottom) != 0)
|
||||
{
|
||||
CreateAliens();
|
||||
// Force game end as at least one of the enemies have reached the bottom!
|
||||
SetGameEnd(GameOverReason.EnemiesReachedBottom);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we didn't find any enemies, then spawn some
|
||||
if ((enemiesResultFlags & UpdateEnemiesResultFlags.FoundEnemy) == 0)
|
||||
{
|
||||
CreateEnemies();
|
||||
m_TickPeriodic.Value = 0.2f;
|
||||
}
|
||||
|
||||
if (foundEdge)
|
||||
// If the enemies reached the either side of the boundaries, then change the movement direction
|
||||
// And move them to the next row below
|
||||
if ((enemiesResultFlags & UpdateEnemiesResultFlags.ReachedHorizontalBoundary) != 0)
|
||||
{
|
||||
m_AlienDirection.Value = -m_AlienDirection.Value;
|
||||
m_EnemyMovingDirection.Value = -m_EnemyMovingDirection.Value;
|
||||
m_TickPeriodic.Value *= 0.95f; // get faster
|
||||
|
||||
var aliensCount = m_Aliens.Count;
|
||||
for (var index = 0; index < aliensCount; index++)
|
||||
var enemiesCount = m_Enemies.Count;
|
||||
for (var index = 0; index < enemiesCount; index++)
|
||||
{
|
||||
var alien = m_Aliens[index];
|
||||
alien.transform.Translate(0, k_AlienVerticalMovementOffset, 0);
|
||||
var enemy = m_Enemies[index];
|
||||
enemy.transform.Translate(0, k_EnemyVerticalMovementOffset, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Saucer == null)
|
||||
if (Random.Range(0, 1.0f) > m_RandomThresholdForSaucerCreation)
|
||||
CreateSaucer();
|
||||
CreateSuperEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpdateShootingEnemies(ref bool foundAlien, ref bool foundEdge)
|
||||
private void UpdateShootingEnemies(ref UpdateEnemiesResultFlags flags)
|
||||
{
|
||||
var aliensCount = m_Aliens.Count;
|
||||
for (var index = 0; index < aliensCount; index++)
|
||||
flags = UpdateEnemiesResultFlags.None;
|
||||
var enemiesCount = m_Enemies.Count;
|
||||
for (var index = 0; index < enemiesCount; index++)
|
||||
{
|
||||
var alien = m_Aliens[index];
|
||||
Assert.IsTrue(alien);
|
||||
|
||||
if (alien.score > 100)
|
||||
var enemy = m_Enemies[index];
|
||||
Assert.IsTrue(enemy);
|
||||
|
||||
// If at least one of the enemies reached bottom, return early.
|
||||
if (enemy.transform.position.y <= k_BottomBoundaryOffset)
|
||||
{
|
||||
flags |= UpdateEnemiesResultFlags.ReachedBottom;
|
||||
return;
|
||||
}
|
||||
|
||||
if (enemy.score > 100)
|
||||
continue;
|
||||
|
||||
foundAlien = true;
|
||||
alien.transform.position += new Vector3(m_AlienDirection.Value, 0, 0);
|
||||
flags |= UpdateEnemiesResultFlags.FoundEnemy;
|
||||
enemy.transform.position += new Vector3(m_EnemyMovingDirection.Value, 0, 0);
|
||||
|
||||
if (alien.transform.position.x > 10 || alien.transform.position.x < -10)
|
||||
foundEdge = true;
|
||||
if (enemy.transform.position.x > k_LeftOrRightBoundaryOffset || enemy.transform.position.x < -k_LeftOrRightBoundaryOffset)
|
||||
flags |= UpdateEnemiesResultFlags.ReachedHorizontalBoundary;
|
||||
|
||||
// can shoot if the lowest in my column
|
||||
var canShoot = true;
|
||||
var column = alien.column;
|
||||
var row = alien.row;
|
||||
for (var otherIndex = 0; otherIndex < aliensCount; otherIndex++)
|
||||
var column = enemy.column;
|
||||
var row = enemy.row;
|
||||
for (var otherIndex = 0; otherIndex < enemiesCount; otherIndex++)
|
||||
{
|
||||
var otherAlien = m_Aliens[otherIndex];
|
||||
Assert.IsTrue(otherAlien != null);
|
||||
var otherEnemy = m_Enemies[otherIndex];
|
||||
Assert.IsTrue(otherEnemy != null);
|
||||
|
||||
if (Math.Abs(otherAlien.column - column) < 0.001f)
|
||||
if (otherAlien.row < row)
|
||||
if (Math.Abs(otherEnemy.column - column) < 0.001f)
|
||||
if (otherEnemy.row < row)
|
||||
{
|
||||
canShoot = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
alien.canShoot = canShoot;
|
||||
enemy.canShoot = canShoot;
|
||||
}
|
||||
|
||||
return foundAlien;
|
||||
}
|
||||
|
||||
public void SetScore(int score)
|
||||
|
@ -366,27 +403,46 @@ public class InvadersGame : NetworkBehaviour
|
|||
|
||||
public void DisplayGameOverText(string message)
|
||||
{
|
||||
if (gameOverText) gameOverText.gameObject.SetActive(true);
|
||||
if (gameOverText)
|
||||
{
|
||||
gameOverText.SetText(message);
|
||||
gameOverText.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetGameEnd(bool isGameOver)
|
||||
public void SetGameEnd(GameOverReason reason)
|
||||
{
|
||||
Assert.IsTrue(IsServer, "SetGameEnd should only be called server side!");
|
||||
|
||||
// We should only end the game if all the player's are dead
|
||||
if (isGameOver)
|
||||
if (reason != GameOverReason.Death)
|
||||
{
|
||||
foreach (NetworkClient networkedClient in NetworkManager.Singleton.ConnectedClientsList)
|
||||
{
|
||||
var playerObject = networkedClient.PlayerObject;
|
||||
if(playerObject == null) continue;
|
||||
|
||||
// We should just early out if any of the player's are still alive
|
||||
if (playerObject.GetComponent<PlayerControl>().IsAlive)
|
||||
return;
|
||||
}
|
||||
this.isGameOver.Value = true;
|
||||
BroadcastGameOverClientRpc(reason); // Notify our clients!
|
||||
return;
|
||||
}
|
||||
this.isGameOver.Value = isGameOver;
|
||||
|
||||
foreach (NetworkClient networkedClient in NetworkManager.Singleton.ConnectedClientsList)
|
||||
{
|
||||
var playerObject = networkedClient.PlayerObject;
|
||||
if(playerObject == null) continue;
|
||||
|
||||
// We should just early out if any of the player's are still alive
|
||||
if (playerObject.GetComponent<PlayerControl>().IsAlive)
|
||||
return;
|
||||
}
|
||||
|
||||
this.isGameOver.Value = true;
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void BroadcastGameOverClientRpc(GameOverReason reason)
|
||||
{
|
||||
var localPlayerObject = NetworkManager.Singleton.ConnectedClients[NetworkManager.Singleton.LocalClientId].PlayerObject;
|
||||
Assert.IsNotNull(localPlayerObject);
|
||||
|
||||
if (localPlayerObject.TryGetComponent<PlayerControl>(out var playerControl))
|
||||
playerControl.NotifyGameOver(reason);
|
||||
}
|
||||
|
||||
public void RegisterSpawnableObject(InvadersObjectType invadersObjectType, GameObject gameObject)
|
||||
|
@ -395,16 +451,16 @@ public class InvadersGame : NetworkBehaviour
|
|||
|
||||
switch (invadersObjectType)
|
||||
{
|
||||
case InvadersObjectType.Alien:
|
||||
case InvadersObjectType.Enemy:
|
||||
{
|
||||
// Don't register if this is a saucer
|
||||
if (gameObject.TryGetComponent<Saucer>(out var saucer))
|
||||
if (gameObject.TryGetComponent<SuperEnemyMovement>(out var saucer))
|
||||
return;
|
||||
|
||||
gameObject.TryGetComponent<AlienInvader>(out var alienInvader);
|
||||
Assert.IsTrue(alienInvader != null);
|
||||
if (!m_Aliens.Contains(alienInvader))
|
||||
m_Aliens.Add(alienInvader);
|
||||
gameObject.TryGetComponent<EnemyAgent>(out var enemyAgent);
|
||||
Assert.IsTrue(enemyAgent != null);
|
||||
if (!m_Enemies.Contains(enemyAgent))
|
||||
m_Enemies.Add(enemyAgent);
|
||||
break;
|
||||
}
|
||||
case InvadersObjectType.Shield:
|
||||
|
@ -426,16 +482,16 @@ public class InvadersGame : NetworkBehaviour
|
|||
|
||||
switch (invadersObjectType)
|
||||
{
|
||||
case InvadersObjectType.Alien:
|
||||
case InvadersObjectType.Enemy:
|
||||
{
|
||||
// Don't unregister if this is a saucer
|
||||
if (gameObject.TryGetComponent<Saucer>(out var saucer))
|
||||
if (gameObject.TryGetComponent<SuperEnemyMovement>(out var saucer))
|
||||
return;
|
||||
|
||||
gameObject.TryGetComponent<AlienInvader>(out var alienInvader);
|
||||
Assert.IsTrue(alienInvader != null);
|
||||
if (m_Aliens.Contains(alienInvader))
|
||||
Assert.IsTrue(m_Aliens.Remove(alienInvader));
|
||||
gameObject.TryGetComponent<EnemyAgent>(out var enemyAgent);
|
||||
Assert.IsTrue(enemyAgent != null);
|
||||
if (m_Enemies.Contains(enemyAgent))
|
||||
Assert.IsTrue(m_Enemies.Remove(enemyAgent));
|
||||
break;
|
||||
}
|
||||
case InvadersObjectType.Shield:
|
||||
|
@ -497,48 +553,48 @@ public class InvadersGame : NetworkBehaviour
|
|||
CreateShield(shieldPrefab, 7, -1);
|
||||
}
|
||||
|
||||
private void CreateSaucer()
|
||||
private void CreateSuperEnemy()
|
||||
{
|
||||
Assert.IsTrue(IsServer, "Create Saucer should be called server-side only!");
|
||||
|
||||
m_Saucer = Instantiate(saucerPrefab, saucerSpawnPoint.position, Quaternion.identity);
|
||||
m_Saucer = Instantiate(superEnemyPrefab, saucerSpawnPoint.position, Quaternion.identity);
|
||||
|
||||
// Spawn the Networked Object, this should notify the clients
|
||||
m_Saucer.GetComponent<NetworkObject>().Spawn();
|
||||
}
|
||||
|
||||
private void CreateAlien(GameObject prefab, float posX, float posY)
|
||||
private void CreateEnemy(GameObject prefab, float posX, float posY)
|
||||
{
|
||||
Assert.IsTrue(IsServer, "Create Alien should be called server-side only!");
|
||||
Assert.IsTrue(IsServer, "Create Enemy should be called server-side only!");
|
||||
|
||||
var newAlien = Instantiate(prefab);
|
||||
newAlien.transform.position = new Vector3(posX, posY, 0.0f);
|
||||
newAlien.GetComponent<AlienInvader>().Setup(Mathf.RoundToInt(posX), Mathf.RoundToInt(posY));
|
||||
var enemy = Instantiate(prefab);
|
||||
enemy.transform.position = new Vector3(posX, posY, 0.0f);
|
||||
enemy.GetComponent<EnemyAgent>().Setup(Mathf.RoundToInt(posX), Mathf.RoundToInt(posY));
|
||||
|
||||
// Spawn the Networked Object, this should notify the clients
|
||||
newAlien.GetComponent<NetworkObject>().Spawn();
|
||||
enemy.GetComponent<NetworkObject>().Spawn();
|
||||
}
|
||||
|
||||
public void CreateAliens()
|
||||
public void CreateEnemies()
|
||||
{
|
||||
float startx = -8;
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
CreateAlien(alien1Prefab, startx, 12);
|
||||
CreateEnemy(enemy1Prefab, startx, 12);
|
||||
startx += 1.6f;
|
||||
}
|
||||
|
||||
startx = -8;
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
CreateAlien(alien2Prefab, startx, 10);
|
||||
CreateEnemy(enemy2Prefab, startx, 10);
|
||||
startx += 1.6f;
|
||||
}
|
||||
|
||||
startx = -8;
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
CreateAlien(alien3Prefab, startx, 8);
|
||||
CreateEnemy(enemy3Prefab, startx, 8);
|
||||
startx += 1.6f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ public class LobbyControl : NetworkBehaviour
|
|||
|
||||
[SerializeField]
|
||||
private string m_InGameSceneName = "InGame";
|
||||
|
||||
// Minimum player count required to transition to next level
|
||||
[SerializeField]
|
||||
private int m_MinimumPlayerCount = 2;
|
||||
|
||||
public Text LobbyText;
|
||||
private bool m_AllPlayersInLobby;
|
||||
|
||||
|
@ -84,8 +89,7 @@ public class LobbyControl : NetworkBehaviour
|
|||
/// </summary>
|
||||
private void UpdateAndCheckPlayersInLobby()
|
||||
{
|
||||
//This is game preference, but I am assuming at least 2 players?
|
||||
m_AllPlayersInLobby = m_ClientsInLobby.Count > 1;
|
||||
m_AllPlayersInLobby = m_ClientsInLobby.Count >= m_MinimumPlayerCount;
|
||||
|
||||
foreach (var clientLobbyStatus in m_ClientsInLobby)
|
||||
{
|
||||
|
|
|
@ -26,12 +26,12 @@ public class PlayerBullet : MonoBehaviour
|
|||
if (!NetworkManager.Singleton.IsServer)
|
||||
return;
|
||||
|
||||
var hitAlien = collider.gameObject.GetComponent<AlienInvader>();
|
||||
if (hitAlien != null && owner)
|
||||
var hitEnemy = collider.gameObject.GetComponent<EnemyAgent>();
|
||||
if (hitEnemy != null && owner)
|
||||
{
|
||||
owner.IncreasePlayerScore(hitAlien.score);
|
||||
owner.IncreasePlayerScore(hitEnemy.score);
|
||||
|
||||
Destroy(hitAlien.gameObject);
|
||||
Destroy(hitEnemy.gameObject);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -166,10 +166,11 @@ public class PlayerControl : NetworkBehaviour
|
|||
if (deltaX != 0)
|
||||
{
|
||||
var newMovement = new Vector3(deltaX, 0, 0);
|
||||
transform.position = Vector3.MoveTowards(transform.position, transform.position + newMovement, m_MoveSpeed * Time.deltaTime);
|
||||
transform.position = Vector3.MoveTowards(transform.position,
|
||||
transform.position + newMovement, m_MoveSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.Space)) ShootServerRPC();
|
||||
if (Input.GetKeyDown(KeyCode.Space)) ShootServerRPC();
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
|
@ -199,15 +200,41 @@ public class PlayerControl : NetworkBehaviour
|
|||
m_IsAlive = false;
|
||||
m_MoveX.Value = 0;
|
||||
m_Lives.Value = 0;
|
||||
InvadersGame.Singleton.SetGameEnd(true);
|
||||
NotifyDeathClientRpc(m_OwnerRPCParams);
|
||||
InvadersGame.Singleton.SetGameEnd(GameOverReason.Death);
|
||||
NotifyGameOverClientRpc(GameOverReason.Death, m_OwnerRPCParams);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
public void NotifyDeathClientRpc(ClientRpcParams clientParams)
|
||||
private void NotifyGameOverClientRpc(GameOverReason reason, ClientRpcParams clientParams)
|
||||
{
|
||||
NotifyGameOver(reason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This should only be called locally, either through NotifyGameOverClientRpc or through the InvadersGame.BroadcastGameOverReason
|
||||
/// </summary>
|
||||
/// <param name="reason"></param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public void NotifyGameOver(GameOverReason reason)
|
||||
{
|
||||
Assert.IsTrue(IsLocalPlayer);
|
||||
m_HasGameStarted = false;
|
||||
InvadersGame.Singleton.DisplayGameOverText("You Are Dead!");
|
||||
switch (reason)
|
||||
{
|
||||
case GameOverReason.None:
|
||||
InvadersGame.Singleton.DisplayGameOverText("You have lost! \n Unknown reason!");
|
||||
break;
|
||||
case GameOverReason.EnemiesReachedBottom:
|
||||
InvadersGame.Singleton.DisplayGameOverText("You have lost! \n The enemies have invaded you!");
|
||||
break;
|
||||
case GameOverReason.Death:
|
||||
InvadersGame.Singleton.DisplayGameOverText("You have lost! \n Your health was depleted!");
|
||||
break;
|
||||
case GameOverReason.Max:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(reason), reason, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using MLAPI;
|
||||
using UnityEngine;
|
||||
|
||||
public class Saucer : MonoBehaviour
|
||||
public class SuperEnemyMovement : MonoBehaviour
|
||||
{
|
||||
private const float k_YBoundary = 14.0f;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"com.unity.ide.rider": "2.0.7",
|
||||
"com.unity.ide.visualstudio": "2.0.7",
|
||||
"com.unity.ide.visualstudio": "2.0.9",
|
||||
"com.unity.ide.vscode": "1.2.3",
|
||||
"com.unity.multiplayer.mlapi": "https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi.git?path=/com.unity.multiplayer.mlapi#release/0.1.0",
|
||||
"com.unity.textmeshpro": "3.0.3",
|
||||
"com.unity.timeline": "1.4.7",
|
||||
"com.unity.textmeshpro": "3.0.6",
|
||||
"com.unity.timeline": "1.4.8",
|
||||
"com.unity.ugui": "1.0.0",
|
||||
"com.unity.modules.ai": "1.0.0",
|
||||
"com.unity.modules.animation": "1.0.0",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.visualstudio": {
|
||||
"version": "2.0.7",
|
||||
"version": "2.0.9",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
@ -49,7 +49,7 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.test-framework": {
|
||||
"version": "1.1.24",
|
||||
"version": "1.1.26",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
@ -60,7 +60,7 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.textmeshpro": {
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.6",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
@ -69,7 +69,7 @@
|
|||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.timeline": {
|
||||
"version": "1.4.7",
|
||||
"version": "1.4.8",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
m_EditorVersion: 2020.3.7f1
|
||||
m_EditorVersionWithRevision: 2020.3.7f1 (dd97f2c94397)
|
||||
m_EditorVersion: 2020.3.13f1
|
||||
m_EditorVersionWithRevision: 2020.3.13f1 (71691879b7f5)
|
||||
|
|
|
@ -6,8 +6,14 @@ EditorUserSettings:
|
|||
serializedVersion: 4
|
||||
m_ConfigSettings:
|
||||
RecentlyUsedScenePath-0:
|
||||
value: 22424703114646680e0b0227036c761e3116152f623d28393930
|
||||
flags: 0
|
||||
RecentlyUsedScenePath-1:
|
||||
value: 22424703114646680e0b0227036c761e1f033a25233c15243f280d7df7ee3d2cfb
|
||||
flags: 0
|
||||
RecentlyUsedScenePath-2:
|
||||
value: 22424703114646680e0b0227036c761e00161c2f3e3b0a3f2f2b047df7ee3d2cfb
|
||||
flags: 0
|
||||
vcSharedLogLevel:
|
||||
value: 0d5e400f0650
|
||||
flags: 0
|
||||
|
|
Загрузка…
Ссылка в новой задаче