[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:
Cosmin 2021-07-13 14:20:33 +01:00
Родитель 61fb994c24
Коммит 5b95753416
28 изменённых файлов: 275 добавлений и 169 удалений

Просмотреть файл

@ -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