Przyspieszony kurs C# pod Unity3d dla leniwych i opornych.

Poprzednie lekcje – Spis Treści

Ostatnio omówiliśmy sobie zmienne i stałe, dzięki którym możemy operować w jakikolwiek sposób na danych. Doszły do tego operatory arytmetyczne, dzięki czemu możemy się bawić podstawami matematyki.

Dzisiaj przejdziemy do jednego z najważniejszych zagadnień, jakimi są funkcję warunkowe. Zagadnienie jest bardzo proste, ale ważne do zapamiętania. Potem omówimy sobie operatory logiczne, który w znaczący sposób, poprawią nasze możliwości korzystania z funkcji warunkowych – mają też inne zastosowania, ale w funkcji warunkowej, przykłady będą najbardziej klarowne.

Funkcje Warunkowe

Funkcja warunkowa, nazywana też instrukcją warunkową. Nie wnikam w to, która nazwa jest poprawna. Nie to jest najważniejsze. Ważne za to jest, po co nam ona? Tak się składa, że jest to chyba najczęstsza i najważniejsza konstrukcja w całym programowaniu. Przynajmniej według mnie.

Jak sama nazwa wskazuje, jest to konstrukcja, która pozwala nam sprawdzić jakiś warunek. Czy punkty życia są równe zero. Czy kolizja nastąpiła z graczem. Czy mamy wystarczająco pieniędzy, żeby coś kupić, itd. Dostępne mamy funkcję warunkowe w 2 wariantach:

IF (ang. Jeżeli)

Najbardziej podstawowa konstrukcja:

if(warunek) {
 // Wykonaj to, jeśli warunek jest prawdą
}

Przykład 1:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start () {
		if (2 == 2) {
			Debug.Log ("TAK");
		}
	}
}

Powyższy kod, powinien nam wypisać TAK, ponieważ niewątpliwie 2 jest równe 2. (kwestię podwójnego równa się, wyjaśnimy w kolejnym rozdziale)

Jednak jest ważna kwestia. Co może być warunkiem? Wszystko. Od takiej prostej konstrukcji jak moja, przez konstrukcję ze zmiennymi, funkcjami, czy nawet bardziej złożone z kilkoma warunkami – to wymaga znajomości operatorów logicznych, więc wrócimy do tego później.

Jednak to nie cała możliwa konstrukcja ifa, bo możemy go nieco rozbudować:

if(warunek1) {
  // Wykonaj, jeśli warunek1 został spełniony
} else if(warunek2) {
  // Wykonaj, jeśli warunek2 został spełniony
} else {
  // Wykonaj, jeśli żaden inny warunek nie został spełniony
}

Sprawdzamy sobie tutaj podstawowy warunek. Jeśli został spełniony, wykonujemy to co jest pod nim, resztę olewamy. W przeciwnym razie, idziemy sobie do pierwszego else if i sprawdzamy jego warunek. Jeśli ten został spełniony, to wykonujemy jego kod, jeśli nie, wędrujemy dalej. Tutaj ważna uwaga! Takich else if, może być nieskończenie wiele! Na samym końcu trafiamy do else, który jest pozbawiony warunku. Ten kod, zostanie wykonany, jeżeli żaden z poprzednich warunków nie został spełniony. Można wykorzystanie zobrazować tak:

if(mam więcej niż 120zł?) {
  Kupuje sobie grę!
} else if(Mam więcej niż 50zł?) {
  Kupie sobie grę z przeceny
} else if(Mam więcej niż 30zł?) {
  Kupie sobie książkę
} else if(Mam więcej niż 1zł?) {
  Kupię sobie batona
} else {
  Nah... idę do pracy
}

Jednak, może bardziej przemówią tutaj ify, które można faktycznie zastosować w grze?

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {

	private int hp = 10;
	private int monety = 4;

	GameObject drzwi;

	void Start () {
		if (hp < 0) {
			Destroy (gameObject);
		} 

		if(monety >= 5) {
			drzwi.SendMessage("otworzDrzwi");
		}
	}
}

Z racji, że nasz obiekt drzwi tutaj jest pusty, skrypt nie zrobi w sumie nic, ale chyba fajnie obrazuje, do jak podstawowych elementów gry, możemy wykorzystać funkcję warunkową.

Mogłoby się wydawać, że to tyle. Ale jest jeszcze jedna konstrukcja warunkowa, używana dość rzadko. Aczkolwiek czasem się przydaje i warto o niej pamiętać.

Switch … Case

Popularny słiczokejs. Jest w nim jedna zasadnicza różnica. Otóż, gdy w ifie, tworzymy sobie dowolne warunki, z dowolnymi zmiennymi w środku (kiedy if pyta o liczbę jabłek, else if, może pytać o gołębie na rynku), o tyle switch jest zależny od jednej zmiennej:

Przykład 2

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {

	private int zmienna = 6;

	void Start () {

		switch(zmienna) {
			case 1:
				Debug.Log ("Jeden");
			break;
			case 2:
				Debug.Log ("Dwa");
			break;
			default:
				Debug.Log ("Cos innego!");
			break;
		}
	}
}

Bawiąc się wartością zmiennej “zmienna”, można sobie szybko załapać zasadę, ale już pędzę z wyjaśnieniem.

Switch, zaczyna się podobnie do samego ifa:

switch(zmienna) {

}

Jedynie, zamiast warunku, podajemy mu konkretną zmienną. Teraz, kod który się wykona, zależy od wartości zmiennej:

case 1:
	// To się wykona gdy zmienna == 1
break;

Warunkiem tutaj jest to, co znajduje się między słowem case, a dwukropkiem. Za to kod, który się wykona, znajduje się między dwukropkiem, a słówkiem break (ma ono więcej zastosowań i omówimy je sobie kiedy indziej).

default:
	// Odpowiednik else
break;

Znajdujące się na końcu default, jest odpowiednikiem else. Jak żadne z powyższych nie spasowało, to wykonaj to, co znajduje się w tym bloku. Oczywiście tak jak w przypadku ifa, ilość bloków nie jest ograniczona. Można wykorzystać jednego case‘a, mogą być 3. Default może być, ale nie musi.

Jeszcze jedno pytanie może się nasunąć. Jakiego typu może być zmienna? Otóż dowolnego. Może być to string, int, float. Ważne jednak jest, że gdy podajemy inta, warunki muszą sprawdzać inta.  Mało tego, sama zmienna, nie do końca musi być konkretną zmienną. Spokojnie można tam wykorzystać np.

  • a + b
  • jakasFunkcja()
  • a + b – c * d + z

Warunek jest jeden. Cokolwiek tam wstawimy, musi dawać rezultat zawsze konkretnego typu. Najbardziej traci to sens, kiedy wynikiem będzie prawda lub fałsz. Wtedy lepszym rozwiązaniem jest if.

Kiedy stosować ifa, a kiedy switcha? Odpowiedź chyba nasuwa się sama. Jeśli warunek może przyjąć kilka wartości jednego typu, to zamiast robić wyliczankę else ifów, to lepiej wykorzystać switcha.

Operatory porównania

Tutaj będzie bardzo prosto i szybko. Wiedza bardzo przydatna, przy korzystaniu z instrukcji warunkowych. Czyli jak porównywać dwie wartości ze sobą?

  • A == B – Czyli równa się. Zwraca prawdę, jeżeli A jest równe B.
  • A != B – Czyli różne. Zwraca prawdę, jeżeli A jest różne od B.
  • A > B / A < B – Czyli większość / mniejszość. Zwraca prawdę, jeżeli A jest większe/mniejsze od B.
  • A >= B – Czyli większość lub równość. Zwraca prawdę, jeżeli A jest większe lub równe B. (takie połączenie > i ==).
  • A <= B – Czyli mniejszość lub równość. Zwraca prawdę, jeżeli A jest mniejsze lub równe B. (takie połączenie < i ==).
  • A typeof B – Czyli sprawdzenie typu. Zwraca prawdę, jeżeli A jest tego samego typu co B.

Przykład podam do ostatniego. Jeżeli A jest intem i B jest intem, zwróci nam true. Jeżeli A to float, a B to int, dostaniemy false. To najczęściej jest wykorzystywane przy sprawdzaniu czy obiekt jest takiej klasy jakiej oczekujemy.

Operatory logiczne

W trakcie poprzedniej lekcji, omawialiśmy operatory arytmetyczne. Czym że są zatem te logiczne? Mają to do siebie, że zwracają zawsze wartość typu bool, czyli prawdę lub fałsz. Wywodzą się one z klasycznej logiki. Jednak o ile w klasycznej logice, operatorów mamy kilka, w informatyce, prym wiodą cztery.

  • ! – Negacja (NOT)  – !A
    Ogólnie zaprzeczenie. Np. jeżeli naszym warunkiem jest if(a == 2) to, gdy zapiszemy to: if(!(a == 2)), wtedy zaprzeczamy zdaniu w nawiasie. Innymi słowy, prawdę dostaniemy, gdy a != 2.
  • && – i (AND) – A && B
    Tzw. suma logiczna. Łatwo to zrozumieć tłumacząc sobie znak. Prawda, jeżeli A i B. Prawdę dostajemy, gdy oba warunki są prawdą. Więc np.: if((a == 2) && (b == 3)), da nam prawdę tylko wtedy gdy i a == 2 i b == 3. Nawet gdy a == 2, ale b != 3, to if nie przejdzie.
  • || – lub (OR) – A || B
    Tzw. iloczyn logiczny. Znów, łatwo to zrozumieć przez tłumaczenie. Prawda, jeżeli A lub B. Prawdę dostaniemy zatem, gdy choć jeden z warunków jest spełniony. Więc np.: if((a == 2) || (b == 3)), da nam prawdę, albo gdy a == 2, albo gdy b == 3. Nie potrzebujemy spełnienia obu!

[stextbox id=”info” defcaption=”true”]Takie zapis operatorów obowiązuje w warunkach! Jeżeli chcielibyśmy je zastosować w luźny sposób, np.:
CzyMogeUzycPrzedmiotu = (sila > 20) & (level > 3)
To, tak jak w powyższym przykładzie, stosujemy pojedynczy symbol. Trzeba pamiętać, że operatory logiczne, możemy stosować tylko przy operacjach logicznych. Czyli np.: 4 & 3, nie ma sensu.
Dodatkowo, przy takim zastosowaniu operatorów logicznych, pojawia się dodatkowy: XOR. Jest to nieco wariacja na temat OR. Tzn. OR daje true, gdy co najmniej jeden warunek jest prawdą. Jeżeli chodzi o XOR, to jest prawdą, tylko gdy jeden z warunków jest prawdą, a drugi fałszem.[/stextbox]
Ale co nam z tej wiedzy? W prosty sposób rozwijamy sobie możliwość korzystania z ifów. Np.:

Przykład 3

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {

	void Start () {
		int a = 3;
		int b = 4;

		if (a == 3 && b == 4) {
			Debug.Log ("true!");
		}
	}
}

Takie proste, a cieszy. Może na koniec przykład, który powinien rozwiązać wszelkie problemy ze zrozumieniem operatów logicznych:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {

	void Start () {
		Debug.Log ("AND & [W warunkach: &&]");
		Debug.Log ("TRUE & TRUE = " + (true & true));
		Debug.Log ("TRUE & FALSE = " + (true & false));
		Debug.Log ("FALSE & TRUE = " + (false & true));
		Debug.Log ("FALSE & FALSE = " + (false & false));

		Debug.Log ("OR | [W warunkach: ||]");
		Debug.Log ("TRUE & TRUE = " + (true | true));
		Debug.Log ("TRUE & FALSE = " + (true | false));
		Debug.Log ("FALSE & TRUE = " + (false | true));
		Debug.Log ("FALSE & FALSE = " + (false | false));

		Debug.Log ("XOR ^");
		Debug.Log ("TRUE & TRUE = " + (true ^ true));
		Debug.Log ("TRUE & FALSE = " + (true ^ false));
		Debug.Log ("FALSE & TRUE = " + (false ^ true));
		Debug.Log ("FALSE & FALSE = " + (false ^ false));

		Debug.Log ("NOT !");
		Debug.Log ("!TRUE = " + (!true));
		Debug.Log ("!FALSE = " + (!false));

	}
}

Wynikiem powinno być coś takiego:

Wynik operacji logicznych
Wynik operacji logicznych

Zadanie domowe

W sumie na dziś to tyle. Tym razem, mamy dość wiedzy, aby móc ją przećwiczyć. Dlatego kilka prostych zadań do rozwiązania. Pamiętaj, że zadania wchodzą również w zakres poprzedniej części.

Odpowiedzi do zadań, pojawią się w kolejnej części.

Kawałek kodu, do losowania, losowej liczby z zakresu:

int rnd = Random.Range (0, 10);

Ten kawałek, losuje nam liczbę z przedziału 0 do 10. Co przyda się, przy niektórych zadaniach.

  1. Wylosuj liczbę z zakresu od 1 do 10. Jeżeli liczba przyjmuje wartość 1, wypisz na konsoli “Jeden”, jeżeli przyjmuje wartość 2, wypisz “Dwa”. Analogicznie dla liczb do 5. Jeżeli będzie to inna liczba, wypisz “Cos innego”.
  2. Wylosuj liczbę z zakresu od 1 do 20. Jeżeli liczba jest parzysta, to wypisz “parzysta”, a w przeciwnym wypadku, wypisz “nieparzysta”.
  3. Wylosuj dwie liczby z dowolnego przedziału. Jeżeli liczba A jest mniejsza od liczby B, wypisz “mniejsza”. Jeżeli liczba A jest większa od liczby B to wypisz “większa”, jeżeli liczby są równe, wypisz “równe”.
  4. Wylosuj 3 liczby i wypisz największą z nich.
  5. Wylosuj 2 liczby z przedziału 1 do 10. Wypisz “tak”, jeżeli pierwsza z nich jest większa od 5, a ich suma, większa od 10.
  6. Wylosuj 3 liczby i wypisz “OK”, jeżeli przynajmniej jedna z nich jest parzysta.

 

Odpowiedzi do zadań domowych:

Na wstępie zaznaczę, że podane niżej rozwiązania, są rozwiązaniami przykładowymi. W programowaniu zawsze jest wiele ścieżek do rozwiązania problemu, jedne są lepsze inne gorsze. Te poniżej nie są ani najlepszymi, ani najgorszymi, są rozwiązaniami, które akurat przyszły mi do głowy. Jeśli rozwiązałeś zadanie inaczej, ale działa, to rozwiązałeś je dobrze. Kody mogą się różnic optymalnością wykorzystania pamięci czy mocy procesora. Dałoby się to sprawdzić obliczając czas wykonania zadania przez oba programy, jednak przy tak trywialnych zadaniach, nie będzie to zauważalna różnica.

Zadanie 1:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start()
	{
		int rnd = Random.Range (0, 10);
		switch (rnd) {
		case 1:
			Debug.Log ("Jeden");
			break;
		case 2:
			Debug.Log ("Dwa");
			break;
		case 3:
			Debug.Log ("Trzy");
			break;
		case 4:
			Debug.Log ("Cztery");
			break;
		case 5:
			Debug.Log ("Piec");
			break;
		default:
			Debug.Log ("Cos innego");
			break;
		}
		Debug.Log (rnd);
	}
}

Zadanie 2:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start()
	{
		int rnd = Random.Range (0, 20);
		if (rnd % 2 == 0) {
			Debug.Log("Parzysta");
		} else {
			Debug.Log("Nieparzysta");
		}
		Debug.Log (rnd);
	}
}

Zadanie 3:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start()
	{
		int rndA = Random.Range (0, 20);
		int rndB = Random.Range (0, 20);
		if (rndA > rndB) {
			Debug.Log("Wieksza");
		} else if(rndA == rndB) {
			Debug.Log("Rowne");
		} else {
			Debug.Log("Mniejsza");
		}
		Debug.Log (rndA + " - " + rndB);
	}
}

Zadanie 4:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start()
	{
		int rndA = Random.Range (0, 20);
		int rndB = Random.Range (0, 20);
		int rndC = Random.Range (0, 20);
		int max = rndA;

		if (max < rndB) {
			max = rndB;
		}

		if (max < rndC) {
			max = rndC;
		}
		Debug.Log (max);
		Debug.Log (rndA + " - " + rndB + " - " + rndC);
	}
}

Zadanie 5:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start()
	{
		int rndA = Random.Range (0, 10);
		int rndB = Random.Range (0, 10);

		if (rndA > 5 && (rndA + rndB) > 10) {
			Debug.Log ("tak");
		}

		Debug.Log (rndA + " - " + rndB);
	}
}

Zadanie 6:

using UnityEngine;
using System.Collections;

public class Lekcja_02 : MonoBehaviour {
	
	void Start()
	{
		int rndA = Random.Range (0, 10);
		int rndB = Random.Range (0, 10);
		int rndC = Random.Range (0, 10);

		if (rndA % 2 == 0 || rndB % 2 == 0 || rndC % 2 == 0) {
			Debug.Log ("OK");
		}

		Debug.Log (rndA + " - " + rndB + " - " + rndC);
	}
}

 

Wstęp – Przygotowanie środowiska, zmienne i stałe, operatory arytmetyczne <- Poprzednia Lekcja

Następna Lekcja -> Pętle (For, While, Do-While), tablice (Array) i kolekcje (ArrayList, HashMap)