📜 ⬆️ ⬇️

Unity game, open source

Black Friday, Black Friday ... tired. I announce my personal White Monday - for a couple of nights I wrote a small game and post its code for general use, with a 90% discount . Why do I need this? Well, I see the following advantages - the very open source code for job search (yes, yes, now I am in active search), read in the comments about my jambs, and finally change the status to Habré.

The essence of the game


The idea came suddenly, and until it flew away, decided to write it down and then embody it. Imagine a cold, dark cell in a certain prison. In it sits, it is not known as well as it is not known by whom, the chained wizard, who every night torments every evil. With his last strength, he creates a small fireball and breathes a semblance of life into it.

Get to know this is Kalcifer, your avatar in this game.


It is to them you will destroy all the filth, which is creeping in the direction of our chained poor fellow.
')
Referring to the code

Explanation of the code


Let's start with the GameManager . He is the head of everything, it is in him that the state of the game changes - Initialization-> GameLoop-> Win or Lose. Single and single, for singleton. Since the game is not a network, simple, and without complex transitions, it was decided to use this pattern. Here is the processing of hits on the player (see below Known problems ), accounting hit points and checking for winnings / losses. There would be a GodObject , but we have too few classes, so I know not all about everyone. At the initialization stage, a pool of objects is created that reflect the animation of damage to the player and the death of enemies. To track the state of hit points, you can subscribe to UpdateHpWizardDelegate or UpdateHpCalciferDelegate . In our case, GUIManager does this to display the current hp on the screen.



By this time, SpawnManager has already compiled a list of enemy spawn points.



a WaveManager uploaded the order of the waves of the creation of enemies. Waves can be configured in two ways: register in the game code or download from the Json file. To edit this Json a custom editor is written: GameDataEditor



You can register the exact number of spawn or indicate that you can create on any free one.

Creation of waves made with the help of sly Korutina:

Spawnwaves
private IEnumerator SpawnWaves() { yield return new WaitForSeconds(firstWaveDelay); while (currentWave < waves.Count) { if (!CheckFinishedCurrentWave()) { var step = waves[currentWave].GetCurrentWaveStep(); if (step != null) { yield return new WaitForSeconds(step.delay); var spawners = step.spawners; if (spawners != null) { foreach (var spawn in spawners) { SpawnPoint spawnPoint; if (spawn.index == Consts.INDEX_RANDOM_SPAWN_POINT) { spawnPoint = SpawnManager.S_Instance.GetRandomEmptySpawnPoint(); } else { spawnPoint = SpawnManager.S_Instance.GetSpawnPointByIndex(spawn.index); } if (spawnPoint != null) { SpawnManager.S_Instance.SpawnEnemy(spawnPoint, spawn.name); } else { throw new Exception("Not empty spawn point"); } } } Debug.Log(step.text); } waves[currentWave].currentStep++; } else { SelectNextWave(); } } } 


First, firstWaveDelay seconds before the start of the first wave 1. After that, in a cycle, all the waves are driven in turn, inserting the desired delay step.delay between the wave steps. Why in korutina and not for example in Update? Yes, in fact, it is possible this way and that, just here it is more vivid, you can see where the delay ( yield return new WaitForSeconds ) and you don’t have to keep unnecessary cycles and checks.

Let's see what SpawnPoint is all about . This is MonoBehavior with 2 components: SpawnPoint and CircleCollider2D . In the first, with the help of the second, it is determined whether the spawn is busy with some sort of enemy. OnDrawGizmos displays spawn locations in the Unity editor.

OnDrawGizmos

  void OnDrawGizmos() { if (m_IsDirty) { Gizmos.color = Color.red; } else { Gizmos.color = Color.green; } Gizmos.DrawSphere(transform.position, 0.3f); } 




All enemies come from the base class BasicEnemy in which there are several virtual methods:


They can be redefined in descendants, for a different reaction to these events. For example, PoisonEnemy hurts when Calcifer is touched, unlike other enemies.

PoisonEnemy.ContactCalcifer
 public override void ContactCalcifer() { GameManager.S_Instance.DamageCalcifer(damage); base.ContactCalcifer(); } 


By the way, we do not delete enemies and many other objects (many and often created on the scene) using Destroy (this) , but send it back to the object pool - ObjectPool.Recycle (this) . So we save a lot on the creation of objects, which is known to be quite a costly business.

For example, animations end with a call to SelfDestroy () , which returns the animation object back to the pool.



The enemies are moving with the help of the pathos force of the BasicEnemyMoving component. In it, we are interested in two methods: OnEnable () and Move () . OnEnable () is called after pulling the enemy from the pool and is needed to turn the enemy (if necessary) in the direction of the target.

OnEnable
 public void OnEnable() { if (obj!=null&&obj.NeedRotateForDirection) { Vector3 moveDirection = transform.position - GameManager.S_Instance.wizardTransform.position; if (moveDirection != Vector3.zero) { float angle = Mathf.Atan2(moveDirection.y, moveDirection.x) * Mathf.Rad2Deg-90; transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward); } } } 


Move () is a virtual method that moves the enemy to the goal. It can be redefined in descendants and make a special movement (with jerks, sinusoidal, etc.)

Move
 public virtual void Move() { transform.position = Vector3.MoveTowards(transform.position, SpawnManager.S_Instance.TEMP_GOAL.position, speed * Time.deltaTime); } 


On this as if everything.


Known shoals



Todo



Lights animation taken from powstudios.com
For the magician's sprite, a special thank you to cute Kori Tyan

Source: https://habr.com/ru/post/343728/


All Articles