Hallo Habr!
Unter dem Schnitt wartet ein weiterer Artikel auf Sie, in dem erläutert wird, wie ich mir das Ziel gesetzt habe, das Spiel zu programmieren, basierend auf der Übersetzung des Artikels auf dem Habr mit dem Titel Level Design Patterns für 2D-Spiele .
Der Artikel enthält viel Text (sowohl regulär als auch Quelltext) und viele Bilder.
Bevor ich mit meinem ersten Artikel beginne, lass uns dich kennenlernen. Ich heiße Denis. Ich arbeite als Systemadministrator mit einer Gesamterfahrung von 7 Jahren. Ich kann Ihnen nicht sagen, dass ein Systemadministrator eine Art IT-Mitarbeiter ist, der einmal sorgfältig eingesetzt wird und dann über das Flackern verschiedener Zeichen auf einem Monitor nachdenkt. Mit der Zeit kam ich zu dem Schluss, dass es Zeit war, die Grenzen des Wissens zu erweitern und auf Programmierung umzusteigen. Ohne auf Details einzugehen, habe ich versucht, Projekte in C ++ und Python zu erstellen. Aber nach einem Jahr des Studiums kam ich zu dem Schluss, dass die Programmierung meiner Anwendungs- und Systemsoftware nicht meine ist. Aus verschiedenen Gründen.
Nachdem ich tiefer nachgedacht hatte, stellte ich mir die Frage: Was mache ich wirklich gerne mit verschiedenen Arten von Computergeräten? Meine Frage an mich selbst warf mich weit in die Kindheit, nämlich in die glücklichen Stunden, die ich für PS1, PS2, Railroad Tycoon 3 für PC verbracht habe ... Nun, Sie verstehen. Videospiel!
Aufgrund der Anzahl der verschiedenen Schulungsmaterialien fiel die Wahl auf Unity (das Rad nicht neu erfinden?). Nachdem ich einen Monat lang verschiedene Materialien gelesen und angesehen hatte, beschloss ich, das erste sehr einfache Kinderspiel auf den Spielmarkt zu bringen. Angst sozusagen überwinden. Die Veröffentlichung von Anwendungen auf dem Spielemarkt ist doch nicht beängstigend, oder?
Nach ein paar Monaten veröffentlichte ich den Plattformer bereits komplexer. Dann gab es eine Pause (schließlich muss an der Arbeit gearbeitet werden).
Vor ungefähr zwei Wochen sah ich eine Übersetzung eines Artikels auf einem Hub namens Level Design Patterns für 2D-Spiele ( https://habr.com/en/post/456152/ ) und dachte mir - warum nicht? Der Artikel enthält eine einfache und übersichtliche Tabelle mit einer Liste der Inhalte des Spiels, damit es interessant ist. Ich habe die Tabelle freundlicherweise in OneNote kopiert und jedes Muster mit dem Tag Cases (das als abgeschlossen markiert werden kann) markiert.
Was möchte ich als Ergebnis bekommen? Ihre Kritik. Wie ich mir gerne sage - wenn du schwimmen lernen willst, tauche mit deinem Kopf. Wenn Sie denken, dass ich etwas gut gemacht habe, schreiben Sie mir dies in einem Kommentar. Wenn Sie denken, dass ich etwas schlecht gemacht habe - schreiben Sie doppelt darüber.
Ich werde mein langes Programm zum Programmieren eines anderen Plattformers starten.
Avatar
Eine Entität, die von Spielern innerhalb eines Spiels kontrolliert wird. Zum Beispiel Mario und Luigi in Super Mario Bros (Nintendo, 1985).
Es gibt mehrere Unteraufgaben, die implementiert werden müssen, um dem Helden Leben zu geben. Nämlich:
• ( ) • • • •
Um die Animation zu implementieren, müssen wir ein einzelnes Sprite in ein Multi-Sprite konvertieren. Das geht unglaublich einfach. Fügen Sie das Sprite zum Projektordner hinzu und suchen Sie es im Explorer Explorer Unity Explorer. Wenn Sie dann im Inspektorfenster auf das Sprite klicken, wird der Wert der SptiteMode- Eigenschaft von Single in Multiple geändert .

Klicken Sie auf Übernehmen und dann auf SpriteEditor .
Im Sprite-Editor- Fenster müssen Sie jeden Frame der zukünftigen Animation mit der Maus auswählen (siehe Abbildung unten).
Außerdem bietet Unity die Möglichkeit, die Grenzen von Objekten innerhalb des Sprites automatisch hervorzuheben. Dazu müssen Sie im Sprite-Editor- Fenster auf die Schaltfläche Slice klicken. Im Dropdown-Menü sollten Sie Typ => Automatisch, Pivot => Mitte haben . Alles was Sie tun müssen, ist auf die Schaltfläche Slice zu klicken. Danach werden alle Objekte im Sprite automatisch ausgewählt.

Lassen Sie uns diesen Vorgang für alle anderen Animationen ausführen. Als Nächstes müssen Sie die Animationszustände und deren Umschaltung konfigurieren. Dies erfolgt in zwei Schritten. Die erste Aktion, Programmcode.
Erstellen Sie ein leeres Spielobjekt. Klicken Sie dazu mit der rechten Maustaste auf die Registerkarte Hierarchie und wählen Sie im Dropdown-Menü die Option Leer erstellen .

Ein leeres Spielobjekt, das auf der Bühne erstellt wird, hat standardmäßig nur eine Komponente - Transformieren . Diese Komponente bestimmt die Position des Objekts auf der Bühne, den Neigungswinkel und seinen Maßstab.
Sie können das Wort Transformation in zwei verschiedenen Bedeutungen treffen:
Um eine eigene Komponente zu erstellen, klicken Sie auf der Registerkarte des Objektinspektors auf die Schaltfläche Komponente hinzufügen. Als nächstes erscheint ein Suchfeld unter den Standardkomponenten. Es reicht aus, den Namen des zukünftigen Skripts (oder der bereits implementierten Komponente) einzugeben. Wenn keine geeigneten Namen vorhanden sind, bietet Unity Ihnen an, eine neue Komponente zu erstellen. Ich habe diese Komponente HeroScript.cs genannt .

Zunächst beschreiben wir die Felder, in denen Informationen über die visuelle und physische Komponente von Lucas gespeichert werden:
Spoiler Überschrift private Animator animator;
Als nächstes die Felder, die auf die Bewegung des Charakters reagieren:
Spoiler Überschrift Vector3 localScale; bool facingRight = true; [SerializeField] private float dirX, dirY;
Ein Anfang wurde gemacht, ausgezeichnet. Als nächstes wird eine Aufzählung beschrieben und eine Eigenschaft geschrieben, die für das Umschalten des Status der Animation verantwortlich ist. Diese Aufzählung muss außerhalb der Klasse geschrieben werden:
Spoiler Überschrift public enum CharState { idle,
Wir implementieren eine Eigenschaft, die einen neuen Animationsstatus empfängt und festlegt:
Spoiler Überschrift public CharState State { get {
Der Software-Teil ist fertig. Jetzt haben wir eine Aufzählung und eine Eigenschaft, die mit dem Umschalten der Animation verknüpft werden. Als nächstes der zweite Schritt. Im Unity-Editor müssen Sie den Animationsstatus binden und angeben, bei welchen int-Werten sie geändert werden müssen.
Dazu müssen Sie die zuvor erstellten Multi-Sprites einem leeren Spielobjekt zuordnen. Sie müssen lediglich die Frames im Unity Explorer auswählen und auf ein leeres Spielobjekt ziehen, an dem wir zuvor das Skript repariert haben.

Tun Sie dies bei jeder nachfolgenden Animation. Außerdem finden Sie im Explorer mit Animationen das Erscheinungsbild eines Objekts mit einem Blockdiagramm und einer Wiedergabetaste . Durch Doppelklicken wird die Registerkarte Animator geöffnet. Im Inneren sehen Sie mehrere Blöcke mit Animationen. Zunächst sind nur die Eintrittszustände und der erste Satz von Animationen verbunden, die verbunden wurden. AnyState und andere Animationen werden als normale graue Quadrate angezeigt. Um alles zu binden, müssen Sie auf den Status von AnyState klicken und das einzige Dropdown-Menü " Transaktion durchführen" auswählen und an den grauen Block binden. Dieser Vorgang muss für jede Bedingung durchgeführt werden. Am Ende sollten Sie so etwas wie das bekommen, was Sie im Screenshot unten sehen.

Als nächstes müssen Sie explizit angeben, welcher Status genau sein soll, um die erforderliche Animation zu starten. Achten Sie auf den Screenshot, nämlich den linken Teil. Registerkarte Parameter . Darin wird eine Variable vom Typ int State erstellt. Achten Sie als nächstes auf die rechte Seite. Zunächst müssen Sie ab dem Übergang der Animation das Kontrollkästchen Can Transaction To Self deaktivieren. Diese Operation bewahrt Sie vor seltsamen und manchmal völlig unverständlichen Übergängen der Animation zu sich selbst und dem Abschnitt Bedingungen , in denen wir angegeben haben, dass diesem Animationsübergang der Wert 3 der Statusvariablen zugewiesen wurde. Danach weiß Unity, welche Animation ausgeführt werden soll.
Alles ist für die animierte Charakterbewegung erledigt. Lass uns weitermachen.
Der nächste Schritt besteht darin, Lucas beizubringen, sich auf der Bühne zu bewegen. Dies ist vollständig programmiert. Um den Charakter in der Szene zu bewegen, benötigen Sie Schaltflächen, auf die Lucas klickt. Dazu müssen wir auf der Registerkarte " Assets Store" Standard-Assets importieren, aber nicht alle, sondern nur einige zusätzliche Komponenten, nämlich:
• CrossPlatformInput
• Editor
• Umwelt
Nach dem Import des Assets sollte das Unity-Hauptfenster geändert werden und eine zusätzliche Registerkarte Mobile Input wird angezeigt. Wir aktivieren es.
Lassen Sie uns neue UI- Elemente auf den Stage-Control-Buttons erstellen. Erstellen Sie 4 Schaltflächen in jede Richtung. Hoch, runter, vorwärts und rückwärts. In der Bildkomponente weisen wir den Schaltflächen ein Bild zu, das dem Bild entspricht, dh die Fähigkeit, sich zu bewegen. Es sollte ungefähr so aussehen wie der Screenshot unten:

Fügen Sie jeder Schaltfläche eine AxisTouchButton- Komponente hinzu. Dieses Skript enthält nur 4 Felder. Das Feld axisName gibt an , auf welchen Namen beim Aufruf geantwortet werden soll . Das Feld axisValue ist für die Richtung verantwortlich, in die sich Lucas bewegen wird. Das Feld responseSpeed ist dafür verantwortlich, wie schnell Lucas seine Geschwindigkeit entwickeln wird. Das Feld returnToCentreSpeed ist dafür verantwortlich, wie schnell die Schaltfläche in die Mitte zurückkehrt. Lassen Sie die Weiterleiten-Schaltfläche unverändert. Ändern Sie für die Schaltfläche "Zurück" den Wert von axisValue in -1, damit Lucas zurückkehrt. Ändern Sie für die Schaltflächen Nach oben und Nach unten den Achsnamen in Vertikal . Setzen Sie für die Up-Schaltfläche axisValue den Wert für Down -1 auf 1.
Ändern Sie als Nächstes HeroScript.cs . Fügen Sie der using- Direktive einen Namespace hinzu
using UnityStandardAssets.CrossPlatformInput;
Fügen Sie in der Standardstartmethode den folgenden Code hinzu:
Spoiler Überschrift void Start() { localScale = transform.localScale; animator = GetComponent<Animator>();
Wir erstellen eine Methode, die für die Bewegung des Helden verantwortlich ist:
Spoiler Überschrift public void MoveHero() { dirX = CrossPlatformInputManager.GetAxis ("Horizontal") * moveSpeed * Time.deltaTime; dirY = CrossPlatformInputManager.GetAxis ("Vertical") * moveSpeed * Time.deltaTime; transform.position = new Vector2 (transform.position.x + dirX, transform.position.y + dirY); }
Wie Sie sehen können, ist alles einfach. Die Felder dirX und DirY zeichnen Informationen über die Richtung der Achse ( horizontal und vertikal ) auf, multipliziert mit der Geschwindigkeit (die im Editor angegeben werden muss) und multipliziert mit der Laufzeit des letzten Frames.
transform.position schreibt die neue Position in die Transform- Komponente unseres Spielobjekts.
Auf der praktischen Seite des Problems können Sie die Szene ausführen und sehen, wie Lucas in den Abgrund fällt, da sich keine Objekte darunter befinden, die dies verhindern könnten. Lucas ist immer in der Idle- Animation und dreht sich nicht um, wenn wir ihn zurückleiten. Dazu muss das Skript geändert werden. Erstellen Sie eine Methode, die bestimmt, in welche Richtung Lucas schaut:
Spoiler Überschrift void CheckWhereToFace () { if (dirX > 0) { facingRight = true; State = CharState.Walk; } if (dirX < 0) { facingRight = false; State = CharState.Walk; } if (dirX == 0) { State = CharState.idle; } if (dirY < 0) { State = CharState.Walk; } if (dirY > 0) { State = CharState.Walk; } if (((facingRight) && (localScale.x < 0)) || ((!facingRight) && (localScale.x > 0))) localScale.x *= -1; transform.localScale = localScale;
Dieser Teil des Codes ist auch nicht schwierig. Die Methode beschreibt, dass wenn dirX > 0 ist (wenn wir nach rechts gehen), wir das Sprite in diese Richtung drehen und die Laufanimation starten. Wenn weniger als 0, drehen Sie Lucas um 180 Grad und starten Sie die Animation. Wenn dirX Null ist, steht Lucas und Sie müssen die Warteanimation starten.
Warum ist es in diesem Fall vorzuziehen, eine Operation mit Scale zu verwenden, als flipX = true zu verwenden ? In Zukunft werde ich die Fähigkeit beschreiben, Gegenstände in die Hand zu nehmen, und natürlich kann Lucas sich umdrehen und etwas in seinen Händen halten. Wenn ich die übliche Reflexion verwenden würde, würde das Objekt, das ich in meinen Händen halten würde, auf der rechten Seite bleiben (zum Beispiel), wenn Lucas nach links schaut und umgekehrt. Durch Vergrößern wird das Objekt, das Lucas hält, in dieselbe Richtung bewegt, in die sich Lucas selbst gedreht hat.
Wir platzieren die Funktion CheckWhereToFace () in der Funktion Update () , um sie Frame für Frame zu überwachen.
Großartig. Die ersten 2 von 5 Punkten sind abgeschlossen. Kommen wir zu den Bedürfnissen von Lucas. Nehmen wir an, Lucas wird drei Arten von Bedürfnissen haben, die erfüllt werden müssen, um am Leben zu bleiben. Dies ist der Lebensstandard, der Hunger und der Durst. Dazu müssen Sie ein einfaches und verständliches Panel mit einem Indikator für jedes Element erstellen. Um ein solches Panel zu erstellen, klicken Sie mit der rechten Maustaste und wählen Sie UI => Panel .
Markieren wir es ungefähr wie unten gezeigt

Das Panel besteht aus drei Bildern (Bild) für jeden Bedarf (links). Rechts ist das Panel selbst. Auf der ersten Ebene (wir werden es so ausdrücken) befindet sich ein Farbindikator (Bild) ohne Transparenz. Darunter wird ein Bildobjekt kopiert, das transparent ist. Dieses Bild ist für das Original halbtransparent. Außerdem hat Image, das keine Transparenz aufweist, die Eigenschaft Image Type = Filled . Mit dieser Funktion können wir eine Abnahme der Fülle der Bedarfsskala simulieren.


Definieren Sie neue statische Variablen:
Spoiler Überschrift [SerializeField] public static float Health = 100, Eat = 100, Water = 100, _Eat = 0.05f, _Water = 0.1f;
In diesem Fall verwende ich statische Felder. Dies geschieht so, dass diese Felder für die gesamte Klasse eindeutig sind. Auf diese Weise können wir auch direkt über den Klassennamen auf diese Felder zugreifen. Wir schreiben einige einfache Funktionen:
Spoiler Überschrift private float fEat(float x) { Eat = Eat - x * Time.deltaTime; iEat.fillAmount = Eat / 100f;
Dann schreiben wir eine Methode, die Informationen über den Wunsch nach Essen und Trinken sammelt:
Spoiler Überschrift private void Needs() { if (fEat(_Eat) < 0) { Debug.Log(Eat); } else if (fEat(0) == 0) { StartCoroutine(ifDie()); } if (fWater(_Water) < 0) { Debug.Log(Water); } else if (fWater(0) == 0) { StartCoroutine(ifDie()); }
Die Funktion Needs () wird in die Funktion Update () eingefügt und jeder Frame wird aufgerufen. Dementsprechend in den Zeilen
if (fEat(_Eat) < 0)
Es wird eine Funktion aufgerufen, die als Parameter festlegt, wie viel aus den Variablen Eat und Water entnommen werden soll. Wenn das Ergebnis der Funktion nicht 0 ist, ist Lucas noch nicht an Durst oder Hunger gestorben. Wenn Lucas an Hunger oder tödlichen Wunden stirbt, dann machen wir Coroutine
StartCoroutine(ifDie());
Dadurch wird die Todesanimation gestartet und das Level neu gestartet:
Spoiler Überschrift IEnumerator ifDie() { State = CharState.Die; yield return new WaitForSeconds(2); SceneManager.LoadScene("WoodDay", LoadSceneMode.Single); }
Harte Fliese
Ein Spielobjekt, das dem Spieler nicht erlaubt, es zu passieren. Beispiel: Geschlecht in Super Mario Bros (Nintendo, 1985).
Um die Erde zu erkennen und zu verhindern, dass Lucas durch sie fällt, müssen Sie die Komponenten BoxCollider2D und Rigidbody2D an Lucas anschließen. Außerdem benötigen Sie ein Sprite der Erde, auf dem sich die BoxCollider2D- Komponente befindet . Die BoxCollider2D- Komponente implementiert Collider und deren Kollisionsverhalten. Zu diesem Zeitpunkt brauchen wir nichts anderes, als das Versagen von Lucas im Untergrund zu verhindern. Optional können wir nur die Ränder des Colliders bearbeiten. In meinem Fall hat das Bodensprite eine Grasoberfläche und damit es nicht so aussieht, als ob das Gras das Gewicht von Lucas tragen kann, werde ich die Ränder der Komponente bearbeiten.

Nun ein aufregender Levelmarkierungsprozess. Der Einfachheit halber können Sie diesen Landwürfel in ein Fertighaus exportieren. Ein Fertighaus ist ein Container eines Spielobjekts, bei dessen Änderung Sie automatisch Änderungen an allen aus diesem Fertighaus erstellten Spielobjekten vornehmen können. Klonen Sie anschließend dieses Fertighaus mit STRG + D (nachdem Sie es auf der Registerkarte Hierarchie ausgewählt haben) und platzieren Sie es auf der Bühne.

Bildschirm
Der Teil des Levels / der Welt des Spiels, der / die derzeit für den Spieler sichtbar ist.
Richten Sie eine Kamera ein, die dem Player folgt, um einen Teil der Szene anzuzeigen. Als nächstes wird ein sehr einfaches Skript implementiert:
Spoiler Überschrift public GameObject objectToFollow; public float speed = 2.0f; void Update () { CamFoll(); } private void CamFoll() { float interpolation = speed * Time.deltaTime; Vector3 position = this.transform.position; position.y = Mathf.Lerp(this.transform.position.y, objectToFollow.transform.position.y, interpolation); position.x = Mathf.Lerp(this.transform.position.x, objectToFollow.transform.position.x, interpolation); this.transform.position = position; }
Im Feld objectToFollow des Typs GameObject wird ein zu überwachendes Objekt zugewiesen, und im Feld speed die Geschwindigkeit, mit der es erforderlich ist, sich reibungslos hinter dem zugewiesenen GameObject zu bewegen.
Die Informationen zur Bewegungsgeschwindigkeit seit dem letzten Bild werden im Interpolationsfeld aufgezeichnet. Als nächstes wird die Lerp-Methode verwendet, die eine reibungslose Bewegung der Kamera hinter Lucas gewährleistet, wenn sie sich entlang der X- und U-Achse bewegt. Leider kann ich die Funktionsweise der Linie nicht erklären
position.y = Mathf.Lerp(this.transform.position.y, objectToFollow.transform.position.y, interpolation);
in Bezug auf die Mathematik. Daher sage ich einfacher: Diese Methode verlängert die Ausführungszeit jeder Aktion. In unserem Fall ist dies die Bewegung der Kamera hinter dem Objekt.
Gefahr
Spoiler ÜberschriftEntitäten, die den Spieler daran hindern, seine Aufgabe zu erfüllen. Beispiel: Spikes von 1001 Spikes (Nicalis und 8bits Fanatics, 2014).
Lassen Sie uns etwas hinzufügen, das Lukas nicht nur daran hindert, die Bühne bis zum Ende zu durchlaufen, sondern auch die Anzahl seiner Leben und die Möglichkeit des Sterbens beeinflusst (zum einen werden wir das fünfte Teilproblem implementieren, um die Lebensgeschichte von Lucas umzusetzen - der Held kann getötet werden oder sterben).
In diesem Fall verteilen wir die Stacheln auf der Bühne, die sich hinter der Vegetation verbirgt, und nur die Aufmerksamkeit des Spielers hilft beim Vorbeigehen.
Erstellen Sie ein leeres GameObject und verbinden Sie die SpriteRenderer- und PolygonCollider2D- Komponenten damit. In der SpriteRenderer- Komponente verbinden wir das Sprite der Stacheldrahtschaltfläche oder eines anderen Objekts wie gewünscht. Weisen Sie dem Spike außerdem tag = Thorn zu .
Als nächstes erstellen wir auf dem Lucas GameObject ein Skript, das dafür verantwortlich ist, was mit ihm passiert, wenn Lucas mit anderen Kollidern kollidiert. In meinem Fall habe ich es ColliderReaction.cs genannt
Spoiler Überschrift private Rigidbody2D rb2d; void Start() { rb2d = GetComponent<Rigidbody2D>(); } public void OnTriggerEnter2D(Collider2D collision) { switch (collision.gameObject.tag) { case "Thorn": { rb2d.AddForce(transform.up * 4, ForceMode2D.Impulse); HeroScript.Health = HeroScript.Health - 5; } break; } }
Das Wesentliche des Skripts ist so einfach wie 2x2. Wenn ein Thorn- Tag mit einem Spielobjekt kollidiert , wird die Switch- Anweisung mit den von uns angegebenen Kandidaten verglichen. In unserem Fall ist es vorerst Thorn . Zuerst kotzt Lucas, dann wenden wir uns einer statischen Variablen zu und nehmen Lucas 5 Lebenseinheiten ab. Mit Blick auf die Zukunft kann ich sagen, dass es sinnvoll ist, dasselbe für einen Konflikt mit Feinden zu beschreiben:
Spoiler Überschrift case "Enemy": { rb2d.AddForce(transform.up * 2, ForceMode2D.Impulse); HeroScript.Health = HeroScript.Health - 10; } break;
Als nächstes schlage ich vor, zwei Fliegen mit einer Klappe zu schlagen.
Gesammelter Gegenstand und Regel.
Ein Spielobjekt, das die Spieler aufnehmen können.
Wir schlagen die Regel vor, dass, wenn Lucas zwischen die Inseln gehen und hochklettern möchte, Sie einen Baum sammeln müssen, um Brücken und Treppen zu bauen.
Nach bereits bestandenen Methoden werden wir einen Baum und eine Treppe erstellen.
Wir verbinden ein Skript mit dem Baum, das dafür verantwortlich ist, wie viele Protokolle Sie daraus abklopfen können, wenn Sie anfangen, es zu hacken. Da im Sprite-Set nur eine Animation des Angriffs vorgeschlagen wurde, werden wir diese verwenden, wenn wir den Baum fällen (Produktionskosten).
Das Skript im Baum:
Spoiler Überschrift [SerializeField] private Transform inst;
Wenn das Level beginnt, schreiben wir einen zufälligen Wert in fireWood :
Spoiler Überschrift void Awake() { fireWood = Random.Range(4,10); }
Beschreibt eine Methode mit einem Parameter, der dafür verantwortlich ist, wie viele Protokolle auf einen Schlag fallen:
Spoiler Überschrift public int fireWoodCounter(int x) { for (int i = 0; i < fireWood; i++) { fireWood = fireWood - x; InstantiateFireWood(); } return fireWood; }
Eine Methode, mit der Protokollklone auf der Bühne erstellt werden.
private void InstantiateFireWood ():
Spoiler Überschrift { Instantiate(FireWoodPref, inst.position, inst.rotation); }

Erstellen wir ein Protokoll und verbinden Sie ein Skript mit dem folgenden Code:
Spoiler Überschrift public void OnTriggerEnter2D(Collider2D collision) { switch (collision.gameObject.tag) { case "Player": { if (InventoryOnHero.woodCount > 10) { Debug.Log(" !"); } else { InventoryOnHero.woodCount = InventoryOnHero.woodCount + 1; Destroy(this.gameObject); } } break; } }
Als nächstes erstellen wir auch eine Klasse, die für das Inventar verantwortlich ist.
Überprüfen Sie zunächst, ob in der Tasche Platz ist. Wenn nicht, dann bleibt der Fehler und das Protokoll liegen, wenn Platz vorhanden ist, füllen wir das Inventar um eine Einheit auf und zerstören das Protokoll.
Als nächstes müssen Sie etwas mit diesen Ressourcen tun. Wie oben erwähnt, bieten wir dem Spieler die Möglichkeit, Brücken und Treppen zu bauen.
Um eine Brücke zu erstellen, benötigen wir 2 Fertighäuser mit der linken und rechten Hälfte der Brücke. BoxCollider2D . , , - , .
:
Spoiler Überschrift [SerializeField] private Transform inst1, inst2;
:
Spoiler Überschrift public void BuildBridge() { if (InventoryOnHero.woodCount == 0) { Debug.LogWarning (" !"); } if (InventoryOnHero.woodCount > 0) { BridgeCount = BridgeCount - 1; InventoryOnHero.woodCount = InventoryOnHero.woodCount - 1; } switch (BridgeCount) { case 5: Inst1(); break; case 0: Inst2(); break; default: Debug.LogWarning("- "); break; } }
, , . , 10 , 12 8.
, , , , . , 1 , 1 . , 5, , . 0, . , .
.
, ColliderReaction.cs :
Spoiler Überschrift void OnTriggerStay2D(Collider2D collision) { switch (collision.gameObject.tag) { case "Ladder": { rb2d.gravityScale = 0; } break; } } void OnTriggerExit2D(Collider2D collision) { switch (collision.gameObject.tag) { case "Ladder": { rb2d.gravityScale = 1; } break; } }
OnTriggerStay2D , . , 0. , . OnTriggerExit2D , .
, .
19 , . , , , , , .
GO, SpriteRenderer , BoxCollider2D , Rigidbody2D . , — , . , ru.stackoverflow.com.

Trees .
, . , -, , Raycast 2 (4 ). , , , ( ). ( ), . , . , , . , . , , ( , ).
, . , - .
:
Spoiler Überschrift [SerializeField] private GameObject area; private bool m1 = true, m2;
Update() , . , 3 , . 3, , .

, .
Spoiler Überschrift private void fSunFlower() { canBullet = canBullet - minus * Time.deltaTime; if (canBullet <= 0 && SR.flipX == false) { GameObject newArrow = Instantiate(sunFlowerBullet) as GameObject; newArrow.transform.position = transform.position; Rigidbody2D rb = newArrow.GetComponent<Rigidbody2D>(); rb.velocity = sunFlowerTrans.transform.forward * -sunFlowerBulletSpeed; canBullet = 2; } if (canBullet <= 0 && SR.flipX == true) { GameObject newArrow = Instantiate(sunFlowerBullet) as GameObject; newArrow.transform.position = transform.position; Rigidbody2D rb = newArrow.GetComponent<Rigidbody2D>(); rb.velocity = sunFlowerTrans.transform.forward * sunFlowerBulletSpeed; canBullet = 2; }
canBullet = canBullet - minus * Time.deltaTime;
, .
Spoiler Überschrift if (canBullet <= 0 && SR.flipX == false) { GameObject newArrow = Instantiate(sunFlowerBullet) }
, , , , :
Spoiler Überschrift public int Damage(int x) { Health = Health - x; return Health; }
, , :
Spoiler Überschrift public void ifDie() { if (Damage(0) <= 0) { Destroy(this.gameObject); } }
0, .
, :
Spoiler Überschrift if (bGreenMonster) { fGreenMonster(); } if (bSunFlower) { fSunFlower(); }
, .

.
, ?
, .
:

:
Spoiler Überschrift [SerializeField] private Transform Hero;
, :
Spoiler Überschrift private void AttackBtn() { if (CrossPlatformInputManager.GetButtonDown("Attack")) { GameObject.Find("Hero").GetComponent<HeroScript>().State = CharState.AttackA; Collider2D[] Trees = Physics2D.OverlapCircleAll(Hero.position, distWhatHeroSee, Tree); for (int i = 0; i < Trees.Length; i++) { Trees[i].GetComponent<TreeControl>().fireWoodCounter(1); Debug.Log("Trees Collider"); HeroScript.Water = HeroScript.Water - 0.7f; }
GameObject.Find("Hero").GetComponent<HeroScript>().State = CharState.AttackA;
, .
, :
Spoiler Überschrift Collider2D[] Trees = Physics2D.OverlapCircleAll(Hero.position, distWhatHeroSee, Tree); for (int i = 0; i < Trees.Length; i++) { Trees[i].GetComponent<TreeControl>().fireWoodCounter(1); Debug.Log("Trees Collider"); HeroScript.Water = HeroScript.Water - 0.7f; }
Trees , . , , , . .
, . Simple as that!
, - :

, — .
, . , , , .
2 , .
Viel Glück
.
https://opengameart.org/ , :