How to develop another platformer using Unity. Another tutorial, part 2

  • Tutorial
In continuation of the first article ( it is here ), I continue to develop a platformer based on the article “Level Design Patterns for 2D Games” .

After the release of the first article, it was unequivocally decided that the button control that was described in it was completely not convenient. Therefore, the controls in the game were redone on the joystick. Further, unfortunately the game did not pass moderation in the playmarket. Last Friday I received a notification that the project was rejected due to the collection of metadata. By the way, my first platformer “Knight Kadavar” was also rejected for the first time because of an alleged request for permission to manage calls and view SMS (which was utter stupidity on the part of their bot. The game did not require any permissions). Google then demanded from me a written notice about why I need it. But, it all ended with the fact that I fixed a couple of errors that I noticed and sent the game to re-moderation. She was successfully added to the playmarket. Now,

So, as soon as the second scene was created, the pattern was automatically closed

Scene


a fragment of a level / world based on a concept is usually an overcoming difficulty.


Bonus


a collectible item that has a positive effect on the players We

implement 2 types of bonuses for Lucas:

  • First-aid kit that will drop when you destroy monsters
  • Chests with swords that Lucas will be able to throw at enemies

To implement a bonus with a first aid kit, you must modify the prefabs with monsters. More specifically, indicate the place from which bonuses will appear.



Then, modify the Enemy.cs script:

Spoiler heading
/* Определяем, выпадет ли бонус для игрока при смерти монстра*/
	[SerializeField]
	private GameObject bonusPref; // Префаб бонуса
	[SerializeField]
	private Transform instBonus; // Откуда будет вылетать бонус
	[SerializeField]
	private int isBonus; // Реализуем с помощью рандома, будет ли выдан бонус
	/* Конец определения бонусов*/
public void ifDie()
	{
		if (Damage(0) <= 0)
		{
			isBonus = Random.Range(0,3);
			if (isBonus == 0)
			{
				Instantiate(bonusPref, instBonus.position, instBonus.rotation);
			}
			Destroy(this.gameObject);
		}
	}


We call the function with the returned type Damage (0) and check if Health 0 returns. If so, then we call the random number generator. If the generator stops by choosing the number 0, then we throw the player a bonus and destroy the monster.

Next, we describe what you can do with this bonus. To do this, create its prefab with the components SpriteRenderer, BoxCollider2D and Rigidbody2D. Also, create a script that will be responsible for what needs to be done if the apple collides with the player:

Spoiler heading
public void OnTriggerEnter2D(Collider2D collision)
    {
		switch (collision.gameObject.tag)
        {
            case "Player":
            {
				HeroScript.Health = 100;
				Destroy(this.gameObject);
			}
			break;
		}
	}


View preview video .

Next, we realize the bonus of the loss of swords. It can be implemented according to the same principle as in the first part, log loss was implemented. The interesting part will be that when Lucas picks up the swords, they will need to be thrown only in the visibility range of the opponents, and not when Lucas collects the logs. Indeed, in the form in which the Attack \ item collection button is now implemented, it would do just that. Swords were thrown out in any situation. To do this, we modify the attack / collection script code:

Spoiler heading
 /* Определяем поля для метания меча */
    [SerializeField]
	private GameObject swordPref; // Префаб хранения меча
	[SerializeField]
	private Transform instSword; // Откуда выпускать префаб меча
	[SerializeField]
	private float swordSpeed; // Скорость полёта мечей
	private float attackInBoxX, attackInBoxY; // Для отрисовки куба
/* Конец определения полей для метания мечей */
// ED значит EnemyDamage
Collider2D[] ED = Physics2D.OverlapBoxAll(Hero.position, new Vector2(attackInBoxX, attackInBoxY), 12, lEnemy);
			{
				if (ED.Length > 0)
				{
					if (InventoryOnHero.swordCount  == 0)
					{
						Debug.Log("Нечем стрелять");
					}
					if (InventoryOnHero.swordCount > 0)
					{
						instantiateSword();
						InventoryOnHero.swordCount = InventoryOnHero.swordCount - 1;
					}
					else
					{
						for (int i = 0; i < ED.Length; i++)
						{
							ED[i].GetComponent().Damage(1);
						}
					}
				}
			}


What do these lines of code mean? First, we define an array of colliders and check everything that fell into our cube:

Collider2D[] ED = Physics2D.OverlapBoxAll(Hero.position, new Vector2(attackInBoxX, attackInBoxY), lEnemy);

Pay attention to the parameters passed, they are very different from those used for OvelapCircleAll. Key parameters -

Vector2(attackInBoxX, attackInBoxY)

Then the condition is met, if the collider array is greater than 0, then we check the inventory for swords. If the number of swords is 0, then we do nothing and execute the method
ED[i].GetComponent().Damage(1);
as if it were a regular hit. If greater than 0, then release the sword and reduce the number of swords by 1.

Next, the implementation of the instantiateSword () method;

Spoiler heading
 private void instantiateSword()
    {
		GameObject newArrow = Instantiate(swordPref) as GameObject;
		newArrow.transform.position = instSword.transform.position;
		Rigidbody2D rb = newArrow.GetComponent();
		if (GameObject.Find("Hero").GetComponent().localScale.x > 0)
		{
			rb.velocity = new Vector3(swordSpeed, 0, 0);
		}
		else
		{
			rb.velocity = new Vector3(-swordSpeed, 0, 0);
			newArrow.transform.Rotate(0,0,-180);
		}
	}


If you read the previous article well, you might have noticed that this code resembles a section of code that is responsible for shooting a sunflower. Added lines to this section of code that are responsible for determining the scale of Lucas. That is, he looks left or right:

(GameObject.Find("Hero").GetComponent().localScale.x > 0)

If Lucas is looking to the left, then

rb.velocity = new Vector3(-swordSpeed, 0, 0);
newArrow.transform.Rotate(0,0,-180);

the sword flies to the left and also rotate it 180 degrees.

View preview video .

Performing this action, we automatically closed another 1 pattern:

An object


any entity that appears in the game scene and is able to change its state. Objects include dangers, enemies, bonuses, etc.

Although, most likely, this pattern was implemented earlier when the first monster was programmed.

As a result, at the moment 3 patterns are not implemented:

  • Unreachable area
  • Mechanics
  • Boss

The mechanics at the moment are of great interest to me. Because I can’t tell myself that I understand exactly what different people put into this definition.

Further, an unreachable area . In this issue, I will for some time deal with level design issues. Firstly, because I want to make the third level dark. Most likely a cave or a dungeon. With the ability to realize lighting effects.

Boss ! It will also remain a cherry on the cake. I want to make it as independent as possible and with the ability to change my appearance.

The first part of the article
All game objects were taken from the site https://opengameart.org/ , namely:



Feel free and write comments on Habré or to me at worldofonehero@gmail.com.

Thank you for reading, good luck.

Also popular now: