Jak napisać pierwszą grę na Androida w Javie

Autor: John Stephens
Data Utworzenia: 1 Styczeń 2021
Data Aktualizacji: 19 Móc 2024
Anonim
Jak zacząć TWORZYĆ GRY | Plan dla początkujących
Wideo: Jak zacząć TWORZYĆ GRY | Plan dla początkujących

Zawartość


Istnieje wiele sposobów na stworzenie gry na Androida, a jednym z ważnych sposobów jest zrobienie tego od zera w Android Studio z Javą. Daje to maksymalną kontrolę nad tym, jak chcesz, aby gra wyglądała i zachowywała się, a proces nauczy Cię umiejętności, których możesz używać w wielu innych scenariuszach - niezależnie od tego, czy tworzysz ekran powitalny dla aplikacji, czy po prostu chcesz dodaj kilka animacji. Mając to na uwadze, w tym samouczku pokażemy, jak stworzyć prostą grę 2D za pomocą Androida Studio i Java. Możesz znaleźć cały kod i zasoby w Github, jeśli chcesz śledzić.

Konfiguracja

Aby stworzyć naszą grę, musimy zająć się kilkoma konkretnymi koncepcjami: pętlami, wątkami i płótnami. Na początek uruchom Android Studio. Jeśli nie masz go zainstalowanego, zapoznaj się z naszym pełnym wprowadzeniem do Android Studio, które obejmuje cały proces instalacji. Teraz rozpocznij nowy projekt i upewnij się, że wybrałeś szablon „Puste działanie”. To jest gra, więc oczywiście nie potrzebujesz elementów takich jak przycisk FAB komplikujący sprawy.


Pierwszą rzeczą, którą chcesz zrobić, to zmienić AppCompatActivity do Czynność. Oznacza to, że nie będziemy używać funkcji paska akcji.

Podobnie, chcemy również, aby nasza gra była pełnoekranowa. Dodaj następujący kod do onCreate () przed wywołaniem setContentView ():

getWindow (). setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature (Window.FEATURE_NO_TITLE);

Zauważ, że jeśli napiszesz jakiś kod, który zostanie podkreślony na czerwono, prawdopodobnie oznacza to, że musisz zaimportować klasę. Innymi słowy, musisz powiedzieć Android Studio, że chcesz użyć niektórych instrukcji i je udostępnić. Jeśli po prostu klikniesz gdziekolwiek podkreślone słowo, a następnie naciśnij Alt + Enter, to zrobi to automatycznie!

Tworzenie widoku gry

Możesz być przyzwyczajony do aplikacji, które używają skryptu XML do definiowania układu widoków, takich jak przyciski, obrazy i etykiety. To jest linia setContentView robi dla nas.


Ale znowu, jest to gra, co oznacza, że ​​nie musi mieć okien przeglądarki ani przewijanych widoków recyklera. Zamiast tego chcemy zamiast tego pokazać płótno. W Android Studio płótno jest takie samo jak w sztuce: jest to medium, na którym możemy rysować.

Więc zmień ten wiersz na następujący:

setContentView (nowy GameView (ten))

Przekonasz się, że jest to ponownie podkreślone na czerwono. Ale teraz jeśli naciśniesz Alt + Enter, nie będziesz mieć możliwości zaimportowania klasy. Zamiast tego masz opcję Stwórz Klasa. Innymi słowy, zamierzamy stworzyć własną klasę, która określi, co będzie na płótnie. To pozwala nam rysować na ekranie, a nie tylko wyświetlać gotowe widoki.

Kliknij prawym przyciskiem myszy nazwę pakietu w hierarchii po lewej stronie i wybierz Nowy> Klasa. Pojawi się teraz okno do utworzenia klasy i zadzwonisz GameView. W obszarze SuperClass napisz: android.view.SurfaceView co oznacza, że ​​klasa odziedziczy metody - swoje możliwości - z SurfaceView.

W polu Interfejs (y) napiszesz android.view.SurfaceHolder.Callback. Jak w przypadku każdej klasy, musimy teraz stworzyć naszego konstruktora. Użyj tego kodu:

prywatny wątek MainThread; public GameView (kontekst kontekstowy) {super (kontekst); getHolder (). addCallback (this); }

Za każdym razem, gdy nasza klasa jest wywoływana w celu utworzenia nowego obiektu (w tym przypadku naszej powierzchni), uruchomi konstruktor i utworzy nową powierzchnię. Linia „super” wywołuje nadklasę, w naszym przypadku jest to SurfaceView.

Dodając oddzwanianie, możemy przechwytywać zdarzenia.

Teraz zastąp niektóre metody:

@Override public void surfaceChanged (uchwyt SurfaceHolder, format int, szerokość int, wysokość int) {} @Override public void surfaceCreated (uchwyt SurfaceHolder) {} @Override public void surfaceDestroyed (uchwyt SurfaceHolder) {}

Zasadniczo pozwalają nam to zastąpić metody (stąd nazwa) w nadklasie (SurfaceView). W kodzie nie powinno być już żadnych czerwonych podkreśleń. Miły.

Właśnie stworzyłeś nową klasę i za każdym razem, gdy o tym mówimy, zbuduje płótno, na którym będzie można pomalować grę. Klasy Stwórz przedmioty i potrzebujemy jeszcze jednego.

Tworzenie wątków

Nasza nowa klasa zostanie nazwana MainThread. A jego zadaniem będzie stworzenie wątku. Wątek jest zasadniczo podobny do równoległego rozwidlenia kodu, który może działać jednocześnie z Główny część twojego kodu. Możesz mieć uruchomionych wiele wątków naraz, dzięki czemu rzeczy mogą się zdarzyć jednocześnie, zamiast przestrzegać ścisłej sekwencji. Jest to ważne w grze, ponieważ musimy upewnić się, że działa ona płynnie, nawet gdy dużo się dzieje.

Stwórz swoją nową klasę, tak jak wcześniej, i tym razem będzie się przedłużać Wątek. W konstruktorze po prostu zadzwonimy Wspaniały(). Pamiętaj, to jest super klasa, która jest Wątkiem i która może wykonać dla nas wszystkie ciężkie zadania. To jest jak tworzenie programu do mycia naczyń, które po prostu dzwonią pralka().

Po wywołaniu tej klasy utworzy oddzielny wątek, który będzie odgałęzieniem głównej rzeczy. I to jest z tutaj że chcemy stworzyć nasz GameView. Oznacza to, że musimy również odwoływać się do klasy GameView, a także używamy SurfaceHolder, który zawiera płótno. Więc jeśli płótno jest powierzchnią, SurfaceHolder jest sztalugą. A GameView to wszystko.

Cała rzecz powinna wyglądać tak:

klasa publiczna MainThread rozszerza wątek {private SurfaceHolder surfaceHolder; prywatny GameView gameView; public MainThread (SurfaceHolder surfaceHolder, GameView gameView) {super (); this.surfaceHolder = surfaceHolder; this.gameView = gameView; }}

Schweet. Mamy teraz GameView i wątek!

Tworzenie pętli gry

Mamy teraz surowce potrzebne do stworzenia naszej gry, ale nic się nie dzieje. Tutaj pojawia się pętla gry. Zasadniczo jest to pętla kodu, która krąży w kółko i sprawdza dane wejściowe i zmienne przed narysowaniem ekranu. Naszym celem jest, aby było to jak najbardziej spójne, aby nie występowały zacięcia lub czkawka w liczbie klatek na sekundę, którą zbadam później.

Na razie jesteśmy nadal w MainThread klasy, a my zastąpimy metodę z nadklasy. Ten jest biegać.

I idzie coś takiego:

@Override public void run () {while (running) {canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); synchronized (surfaceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (wyjątek e) {} wreszcie {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (wyjątek e) {e.printStackTrace (); }}}}}

Zobaczysz wiele podkreśleń, dlatego musimy dodać więcej zmiennych i referencji. Wróć na górę i dodaj:

prywatny SurfaceHolder surfaceHolder; prywatny GameView gameView; prywatny bieg boolowski; płótno statyczne publiczne;

Pamiętaj, aby zaimportować płótno. Płótno jest tym, na czym będziemy czerpać. Jeśli chodzi o „lockCanvas”, jest to ważne, ponieważ to właśnie to zasadniczo zamraża płótno, aby umożliwić nam rysowanie na nim. To ważne, ponieważ w przeciwnym razie możesz mieć wiele wątków próbujących z niego korzystać jednocześnie. Po prostu wiedz, że aby edytować płótno, musisz najpierw zamek płótno.

Aktualizacja to metoda, którą zamierzamy stworzyć i to właśnie tam będą zabawne rzeczy.

The próbować i złapać tymczasem są po prostu wymagania dotyczące Javy, które pokazują, że jesteśmy gotowi spróbować obsłużyć wyjątki (błędy), które mogą wystąpić, jeśli płótno nie jest gotowe itp.

Wreszcie, chcemy móc rozpocząć nasz wątek, gdy jest to potrzebne. Aby to zrobić, potrzebujemy tutaj innej metody, która pozwoli nam wprawić w ruch. Właśnie to bieganie zmienna jest dla (należy zauważyć, że wartość logiczna jest typem zmiennej, która jest zawsze prawdziwa lub fałszywa). Dodaj tę metodę do MainThread klasa:

public void setRunning (boolean isRunning) {running = isRunning; }

Ale w tym momencie należy jeszcze podkreślić jedną rzecz i to aktualizacja. Jest tak, ponieważ nie stworzyliśmy jeszcze metody aktualizacji. Więc wróć do GameView i teraz dodaj metodę.

public void update () {}

Musimy także początek groźba! Zrobimy to w naszym SurfaceCreated metoda:

@Override public void surfaceCreated (uchwyt SurfaceHolder) {thread.setRunning (true); thread.start (); }

Musimy również zatrzymać nić, gdy powierzchnia zostanie zniszczona. Jak można się domyślić, zajmiemy się tym w SurfaceDestroyed metoda. Ale biorąc pod uwagę fakt, że może to wymagać wielu prób zatrzymania wątku, zamierzamy zapętlić to i użyć próbować i złapać jeszcze raz. Tak jak:

@Override public void surfaceDestroyed (SurfaceHolder holder) {boolean retry = true; while (retry) {try {thread.setRunning (false); thread.join (); } catch (InterruptedException e) {e.printStackTrace (); } retry = false; }}

I wreszcie, przejdź do konstruktora i upewnij się, że utworzyłeś nową instancję swojego wątku, w przeciwnym razie otrzymasz przerażający wyjątek wskaźnika zerowego! A potem sprawimy, że GameView będzie zogniskowany, co oznacza, że ​​może obsługiwać zdarzenia.

thread = new MainThread (getHolder (), this); setFocusable (true);

Teraz możesz Wreszcie faktycznie przetestuj to! Zgadza się, kliknij uruchom i gotowe powinien faktycznie działają bez żadnych błędów. Przygotuj się na wysadzenie w powietrze!

To… to… pusty ekran! Cały ten kod. Dla pustego ekranu. Ale to jest pusty ekran okazja. Przygotowujesz się do działania dzięki pętli do obsługi zdarzeń. Teraz pozostało tylko sprawić, by coś się wydarzyło. Nie ma nawet znaczenia, że ​​do tej pory nie postępowałeś zgodnie z instrukcjami zawartymi w samouczku. Chodzi o to, że możesz po prostu przetworzyć ten kod, aby zacząć tworzyć wspaniałe gry!

Robienie grafiki

Właśnie, teraz mamy pusty ekran do rysowania, wszystko, co musimy zrobić, to na nim rysować. Na szczęście to prosta część. Wszystko, co musisz zrobić, to zastąpić metodę losowania w naszym GameView klasy, a następnie dodaj ładne zdjęcia:

@Override public void draw (Canvas canvas) {super.draw (canvas); if (canvas! = null) {canvas.drawColor (Color.WHITE); Farba malarska = nowa farba (); paint.setColor (Color.rgb (250, 0, 0)); canvas.drawRect (100, 100, 200, 200, farba); }}

Uruchom to, a powinieneś teraz mieć ładny czerwony kwadrat w lewym górnym rogu ekranu, który byłby biały. To z pewnością poprawa.

Możesz teoretycznie stworzyć prawie całą grę, umieszczając ją w tej metodzie (i zastępując) onTouchEvent do obsługi danych wejściowych), ale nie byłby to strasznie dobry sposób na załatwienie różnych spraw. Umieszczenie nowej farby w naszej pętli znacznie spowolni rzeczy, a nawet jeśli umieścimy to gdzie indziej, dodając zbyt dużo kodu do rysować metoda byłaby brzydka i trudna do naśladowania.

Zamiast tego rozsądniej jest traktować obiekty gry własnymi klasami. Zaczniemy od takiego, który pokazuje postać i ta klasa zostanie powołana CharacterSprite. Śmiało i zrób to.

Ta klasa narysuje duszka na płótnie i tak będzie wyglądać

klasa publiczna CharacterSprite {prywatny obraz bitmapowy; public CharacterSprite (Bitmap bmp) {image = bmp; } public void draw (Canvas canvas) {canvas.drawBitmap (image, 100, 100, null); }}

Teraz, aby tego użyć, musisz najpierw załadować mapę bitową, a następnie wywołać klasę z GameView. Dodaj odniesienie do private CharacterSprite characterSprite a następnie w SurfaceCreated dodaj metodę:

characterSprite = new CharacterSprite (BitmapFactory.decodeResource (getResources (), R.drawable.avdgreen));

Jak widać, ładowana przez nas mapa bitowa jest przechowywana w zasobach i nazywa się avdgreen (była z poprzedniej gry). Teraz wystarczy przekazać tę mapę bitową do nowej klasy w rysować metoda z:

characterSprite.draw (canvas);

Teraz kliknij uruchom i powinieneś zobaczyć swoją grafikę na ekranie! To jest BeeBoo. Rysowałem go w swoich szkolnych podręcznikach.

Co jeśli chcielibyśmy zmusić tego małego faceta do ruchu? Proste: po prostu tworzymy zmienne xiy dla jego pozycji, a następnie zmieniamy te wartości w aktualizacja metoda.

Więc dodaj odniesienia do swojego CharacterSprite a następnie narysuj swoją mapę bitową w x, y. Utwórz tutaj metodę aktualizacji, a teraz spróbujemy:

y ++;

Za każdym razem, gdy uruchomi się pętla gry, przesuniemy postać w dół ekranu. Zapamiętaj, y współrzędne są mierzone od góry tzw 0 jest u góry ekranu. Oczywiście musimy zadzwonić aktualizacja metoda w CharacterSprite z aktualizacja metoda w GameView.

Naciśnij ponownie Play, a zobaczysz, że Twój obraz powoli przesuwa się po ekranie. Nie zdobywamy jeszcze żadnych nagród, ale to dopiero początek!

OK, żeby robić rzeczy nieco co ciekawsze, po prostu upuszczę tutaj kod „bouncy ball”. Spowoduje to, że nasza grafika odbije się od krawędzi ekranu, podobnie jak stare wygaszacze ekranu Windows. Te dziwnie hipnotyczne.

public void update () {x + = xVelocity; y + = yVelocity; if ((x & screenWidth - image.getWidth ()) || (x <0)) {xVelocity = xVelocity * -1; } if ((y & screenHeight - image.getHeight ()) || (y <0)) {yVelocity = yVelocity * -1; }}

Musisz także zdefiniować te zmienne:

private int xVelocity = 10; private int yVelocity = 5; private int screenWidth = Resources.getSystem (). getDisplayMetrics (). widthPixels; private int screenHeight = Resources.getSystem (). getDisplayMetrics (). heightPixels;

Optymalizacja

Jest dużo więcej do zagłębienia się tutaj, od obsługi danych gracza, przez skalowanie obrazów, po zarządzanie dużą liczbą postaci poruszających się po ekranie jednocześnie. W tej chwili postać podskakuje, ale jeśli przyjrzysz się jej uważnie, zauważysz lekkie jąkanie. To nie jest straszne, ale fakt, że można to zobaczyć gołym okiem, jest czymś w rodzaju znaku ostrzegawczego. Prędkość różni się również w zależności od urządzenia fizycznego. Teraz wyobraź sobie, co się dzieje, kiedy masz mnóstwo od razu na ekranie!

Istnieje kilka rozwiązań tego problemu. Na początek chcę utworzyć prywatną liczbę całkowitą MainThread i nazwij to targetFPS. Będzie to miało wartość 60.Postaram się, aby moja gra działała z tą prędkością, a tymczasem sprawdzę, czy tak jest. W tym celu chcę również prywatnego podwójnego o nazwie średnia FPS.

Zamierzam również zaktualizować biegać metoda w celu zmierzenia, jak długo trwa każda pętla gry, a następnie pauza tę pętlę gry tymczasowo, jeśli wyprzedza docelową liczbę klatek na sekundę. Następnie obliczymy, jak długo to potrwa teraz wziął, a następnie wydrukował to, abyśmy mogli zobaczyć to w dzienniku.

@Override public void run () {long startTime; długi czas Millis; długi czas oczekiwania; long totalTime = 0; int frameCount = 0; long targetTime = 1000 / targetFPS; while (running) {startTime = System.nanoTime (); canvas = null; try {canvas = this.surfaceHolder.lockCanvas (); synchronized (surfaceHolder) {this.gameView.update (); this.gameView.draw (canvas); }} catch (wyjątek e) {} wreszcie {if (canvas! = null) {try {surfaceHolder.unlockCanvasAndPost (canvas); } catch (wyjątek e) {e.printStackTrace (); }}} timeMillis = (System.nanoTime () - startTime) / 1000000; waitTime = targetTime - timeMillis; spróbuj {this.sleep (waitTime); } catch (wyjątek e) {} totalTime + = System.nanoTime () - startTime; frameCount ++; if (frameCount == targetFPS) {averageFPS = 1000 / ((totalTime / frameCount) / 1000000); frameCount = 0; totalTime = 0; System.out.println (AverageFPS); }}}

Teraz nasza gra próbuje zablokować FPS do 60 i powinieneś zauważyć, że na ogół mierzy dość stały 58-62 FPS na nowoczesnym urządzeniu. Na emulatorze możesz uzyskać inny wynik.

Spróbuj zmienić te 60 na 30 i zobacz, co się stanie. Gra zwalnia i to wszystko powinien teraz przeczytaj 30 w logcat.

Zamykanie myśli

Są też inne rzeczy, które możemy zrobić, aby zoptymalizować wydajność. Jest tutaj świetny post na blogu na ten temat. Spróbuj powstrzymać się od tworzenia nowych instancji Paint lub map bitowych wewnątrz pętli i wykonaj wszystkie inicjalizacje na zewnątrz przed rozpoczęciem gry.

Jeśli planujesz stworzyć następną hitową grę na Androida, to są na pewno łatwiejsze i bardziej wydajne sposoby radzenia sobie w tych dniach. Ale zdecydowanie istnieją scenariusze przypadków użycia, które pozwalają narysować na płótnie, i jest to bardzo przydatna umiejętność, aby dodać do swojego repertuaru. Mam nadzieję, że ten przewodnik nieco pomógł i życzę powodzenia w nadchodzących przedsięwzięciach programistycznych!

KolejnyPrzewodnik dla początkujących po Javie

Nie każdy może śpiewać lub grać na intrumencie. Jednak nauka produkcji muzyki i budowanie rytmów dla klubów lub artytów może wciąż na to pozwolić dotknij wojego wewnętrznego muzyka. Moż...

2019 może być twój rok produktywności. Jeśli chcez przejąć kontrolę nad woim życiem i tać ię bardziej wydajnym, nie jeteś am.Wiele oób budzi ię każdego dnia chętnych przeglądaj wzytko na liś...

Polecany Przez Nas.