Unity3d FPS Tutorial, czyli tworzymy własną grę FPS od podstaw z wykorzystaniem silnika Unity3d.

Temat: Ostatnie szlify i budujemy projekt

Spis treści

Jeżeli nie używałeś do tej pory Unity3d:

#0 – Podstawy Podstaw

FPS Tutorial:

#1 – Tworzenie nowego projektu i narzędzie terenu

#2 – Sterowanie postacią

#3 – Życie, pancerz i wytrzymałość postaci

#4 – Regeneracja życia i energii. Efekty trafienia

#5 – Kamera z bronią i strzelanie

#6 – Przeładowanie i amunicja

#7 – Zbieranie przedmiotów

#8 – Druga broń

#9 – Rzut granatem i seria z karabinu

#10 – Przybliżenie i dziury po kulach

#11 – Proste AI przeciwnika

#12 – Animacja postaci przeciwnika, Animator

#13 – Menu główne gry. GUI

– Ostatnie szlify i budujemy projekt

Teoria

Praktycznie, nasza gra jest już gotowa. Jednak dostrzegłem mały błąd w kwestii dodawania amunicji – da się ją dodać tylko do pistoletu. Sprawimy, że będzie to działać niezależnie od broni. Później zajmiemy się dopieszczeniem projektu i jego wydaniem, czyli stworzenie builda gry.

Poprawa amunicji

Do tej pory, amunicję dodawaliśmy bezpośrednio do obiektu Gun, wykorzystując skrypt Shooting.cs. Problem jest taki, że uniemożliwia to dodanie amunicji do innej broni.

Dlatego pierwszy modyfikacji ulega skrypt Ammopack.cs:

using UnityEngine;
using System.Collections;

public class Ammopack : MonoBehaviour {

	public float ammunition = 25.0f;
	public float gunType = 1.0f;

	void Update () 
	{
		transform.Rotate(new Vector3(0, 1, 0));
	}

	void OnTriggerEnter(Collider other)
	{
		if(other.tag.Equals("Player")) {
			GunsInventory inventory = other.GetComponent<GunsInventory>();
			inventory.SendMessage("addAmmo", new Vector2(ammunition, gunType));
			Destroy(gameObject);
		}
	}

}

Gdzie jest różnica? Zmienia się funkcja wysyłająca komunikat. Dokładnie, zmienia się punkt docelowy. Wykorzystujemy sobie skrypt GunsInventory, który będzie pośrednikiem.

W skrypcie Shooting.cs, nie robimy żadnych zmian. Za to, modyfikujemy wspomniany GunsInventory, a dokładniej wzbogacamy go o funkcję addAmmo, która wygląda tak:

public void addAmmo(Vector2 param)
{
	GameObject gun = gunsList[(int)param.y];
	
	if(gun.GetComponent<Shooting>().canGetAmmo()) {
		gun.SetActive(true);
		gun.SendMessage("addAmmo", param);
		if((int)param.y != currentGun) {
			gun.SetActive(false);
		}
	}
}

Teraz obiekt gun, to broń podana przez parametr. Później, pojawia się kod, który uprzednio znajdował się bezpośrednio w kodzie Ammopack.cs. Czyli sprawdzenie, czy możemy podnieść amunicję, a jeśli tak, to wysyłamy parametry do konkretnej broni. Ciekawy jest ostatni if oraz fragment ustawiający broń na aktywną. Po co to? Jeżeli zbieramy amunicję do MP5, trzymając pistolet, MP5 jest nieaktywne, przez co gra nie uznaje jego istnienia. Dlatego, musimy go aktywować. Dzięki zrobieniu tego na tak krótką chwilę, ludzkie oko nie wychwyci, jego pojawienia się.

Ważny jest ostatni if, który wymusza dodanie jeszcze jednej zmiennej prywatnej: currentGun. Otóż, jeśli broń do której dodaliśmy amunicję, jest bronią, którą aktualnie mamy wyciągniętą, to nie chowamy jej. Tak wygląda cały skrypt GunsInventory po zmianach, oznaczyłem zmienione linijki:

using UnityEngine;
using System.Collections;

public class GunsInventory : MonoBehaviour {

	public GameObject[] gunsList = new GameObject[10];

	private bool[] guns = new bool[] {false, true, false, false, false, false, false, false, false, false};
	private KeyCode[] keys = new KeyCode[] {KeyCode.Alpha0, KeyCode.Alpha1, KeyCode.Alpha2, KeyCode.Alpha3, KeyCode.Alpha4, KeyCode.Alpha5, KeyCode.Alpha6, KeyCode.Alpha7, KeyCode.Alpha8, KeyCode.Alpha9};
	private int maxGuns = 1;
	private int currentGun = 1;

	void Update()
	{
		for(int i = 0 ; i < keys.Length ; i++) {
			if(Input.GetKeyDown(keys[i]) && guns[i]) {
				hideGuns();
				gunsList[i].SetActive(true);
				currentGun = i;
			}
		}

	}

	private void hideGuns()
	{
		for(int i = 1 ; i < maxGuns + 1 ; i++) {
			gunsList[i].SetActive(false);
		}
	}
	
	public void addGun(int number)
	{
		guns[number] = true;
		maxGuns++;
	}

	public void addAmmo(Vector2 param)
	{
		GameObject gun = gunsList[(int)param.y];
		
		if(gun.GetComponent<Shooting>().canGetAmmo()) {
			gun.SetActive(true);
			gun.SendMessage("addAmmo", param);
			if((int)param.y != currentGun) {
				gun.SetActive(false);
			}
		}
	}
}

Wypalamy cienie

Unity w wersji darmowej, nie pozwala używać dynamicznych cieni. Trochę smutne, ale pozwala nam stosować cienie statyczne. Nazywa się to wypalaniem cieni. Zasada jest taka, że stosuje się to, jedynie w przypadku obiektów, których wygląd cienia, nie zmieni się przez całą grę.

Pierwszym krokiem, jaki należy zrobić, to przygotować obiekty, które mają być wypalone. Jak mówiłem, wypalane są tylko obiekty statyczne, dlatego należy każdy obiekt, który ma posiadać swój cień ustawić na statyczny. Robimy to, wybierając obiekt i w panelu Inspector zaznaczamy checkbox Static:

Ustawiamy obiekt na statyczny
Ustawiamy obiekt na statyczny

Teraz czas na utworzenie samego wypalania. W tym celu otwieramy okno Lightmapping: [Window -> Lightmapping]. Na pierwszy raz, nie musimy tam grzebać, bo w sumie wiele nam to nie da. Naciskamy Bake Scene i czekamy (Zależnie od ilości obiektów, może to trwać nawet do kilkunastu minut!).

Jeżeli wszystko się powiodło, powinniśmy móc podziwiać cienie. W oknie Scene pojawi się nam małe okienko Lightmap Display. Ma ono ciekawą opcję: Use Lightmaps. Zaznaczając i odznaczając je, możemy zobaczyć jak wygląda różnica.

Warto pamiętać, że cienie pojawią się tylko, jeśli posiadamy źródło światła!

Jeżeli nie pojawiły się cienie przy drzewach, problemem nie jest wypalanie, a same drzewa, które mogą nie być odpowiednio przygotowane.

Dodajemy mgłę

Co możemy zrobić, gdy nasza gra nie powala grafiką? Nieco zadymić atmosferę. Służy do tego mgła. Wchodzimy w [Edit -> Render Settings]. W panelu Inspektor zaznaczamy checkbox opisany jako Fog. Później najlepiej się pobawić opcjami i zobaczyć na własnej skórze co zmieniają, jak działają, a ostateczny efekt zbudować o metodę prób i błędów:

Ustawiamy mgłę
Ustawiamy mgłę

Tworzymy build

Ostatni krok jaki stoi przed nami to przygotowanie gry. W tym celu wchodzimy  w panel: [File -> Build Settings]. Panel jest dość przejrzysty i prosty.

Na samej górze mamy pole Scenes to Build, są to wszystkie sceny, które wejdą w skład gry. Jeżeli tutaj pominiemy jakąś scenę, nie będzie jej w grze. Równie ważne jest, która scena jest sceną zerową. Bo to ona zostanie uruchomiona jako pierwsza.

Pole platform, pozwala zdecydować na jaki system wydamy grę. Aktualnie wybrana platforma oznaczona jest logiem Unity. Aby zmienić platformę, należy wybrać ją z listy i kliknąć “Switch Platform”. Opcji nie omawiam, bo dla każdej platformy są inne. Możemy się do nich łatwo przedostać klikając Player Settings.

Po zbudowaniu gry, normalnie wita nas okno, pozwalające wybrać rozdzielczość ekranu itp. Wiele osób chce je wyłączyć, dlatego wyjaśnię jak to zrobić. Gdy jesteśmy w player settings, wystarczy zmienić opcję Display Resolution Dialog  z Enabled, na dowolną inną.

Ustawienia buildu gry
Ustawienia buildu gry

Oczywiście jest tam wiele więcej opcji, które powinniśmy wypełnić, jeśli grę chcemy wydać na poważnie. Np. ikona, nazwa producenta i nazwa samej gry. Jeżeli jesteśmy zadowoleni z naszych wyborów, klikamy Build, wybieramy katalog docelowy i czekamy. Gdy proces się zakończy, nasza gra będzie gotowa do uruchomienia.

Podsumowanie

Udało nam się! Razem dobrnęliśmy do końca. Nasza gra jest gotowa. Jest bardzo prosta, może wręcz banalna. Jednak tak jak dla pewnie większości z was była to pierwsza gra w Unity3d, dla mnie był to pierwszy tego typu kurs. Później będzie tylko lepiej!

Nie przestawajcie robić gier, a jeśli macie pytania, to jestem do dyspozycji w komentarzach. Jeśli macie fajne pomysły na inne tutoriale zapraszam do rzucania we mnie propozycjami na specjalnej podstronie.

Na sam koniec, rzucam paczką z kompletnym projektem:

https://dl.dropboxusercontent.com/u/7693850/Tutorial_mwinpl.zip

 

Poprzednia część <- #13 – Menu główne gry. GUI