Thursday, December 16, 2010

kasuroid in Camangi Market!

I'm proud to announce that all games where accepted by the Camangi Market. Now you can play the kasuroid games also on tablets. Woohoo! :)

Sunday, December 12, 2010

Earth Defence

The Earth Defence is a fast action game. The story is that the hordes of aliens attacked the Mother Earth. You are the only one who can defend your home. Game starts slowly, but from level to level it gets faster and faster. You have to complete 15 different levels to defend the earth. Launch missiles by tapping the screen to destroy the enemies.

Tuesday, November 23, 2010

Squares Mind

Squares Mind is another memory game. The idea of the game is the same as in the Circles Mind. The main difference is that circles were replaced with squares (not everyone likes circles :)). To complete the level you have to tap the last born square. No ads during the game!


Changes

- no tap between levels
- better touch support
- saving the level record
- other small fixes

- squares animation added in the main menu
- new menu system support

- first version

Tuesday, November 16, 2010

Podsumowanie konkursu

Czas jednak szybko mija :( Właśnie stuknęło 15 tygodni zmagań z blogowaniem i projektem. Zakończył się także "prowodyr" całego zamieszania czyli konkurs "Daj się poznać" :)

Przede wszystkim chciałbym pogratulować wszystkim uczestnikom konkursu wytrwałości. Wiem na swoim przykładzie, że momentami ciężko było zabrać się za pisanie postów. W moim przypadku było to jeszcze o tyle trudniejsze, gdyż odkąd pamiętam pisanie dłuższych tekstów nigdy nie szło mi za dobrze :P

Podsumowanie

Celem projektu kasuroid było zbudowanie środowiska do tworzenia prostych (na początek :P) gier na platformę Android. Dodatkowym przypieczętowaniem sukcesu miał być Astro Warrior - kosmiczna strzelanka.

Coż, kasuroid ciągle się rozwija (wersję 1.0 powinienem wkrótce wrzucić na code.google). Realizacja kolejnych gier umożliwia poprawianie błędów (w tym projektowych) oraz rozwijać framework na bieżąco.

Niestety Astro Warrior będzie musiał trochę jeszcze poczekać z realizacją. Głównie dlatego, że lista tytułów jest z dnia na dzień coraz dłuższa i musiałem nadać pewne priorytety.

Obecnie w markecie są już trzy gry zbudowane na bazie kasuroid: Nuclear Balls, Circles Mind oraz Squares Mind. Na dniach wrzucę także kolejny tytuł, nad którym pracowałem ostatnio. Statystyki gier można podejrzeć m.in. tutaj.

Co zyskałem?

Przede wszystkim wiedzę na temat samego Androida oraz.. dobrowolnych użytkowników swoich gier :) Muszę przyznać, że takie zderzenie z użytkownikami (z którymi de facto nie ma się bezpośredniego kontaktu) bywa frustrujące :) Wielką zaletą jest możliwość dotarcia do potencjalnych użytkowników w bardzo prosty sposób. W przypadku produkcji na PC w obecnych czasach jest to o wiele trudniejsze. Przede wszystkim z racji tego, że na platformy mobilne użytkownicy są niejako "przygotowani" na słabsze produkcje, tj. takie, które nie wymagają kosmicznych technologii i grafiki (chociaż i to się zmienia). W przypadku tytułów na PC userzy są bardziej wybredni.

Co dalej?

Kolejne tytuły i rozwój samego kasuroid. Zobaczymy co się z tego wykluje ;P

Podziękowania

Miało być bez wazeliniarstwa :P ale szczególne podziękowania należą się  Maćkowi, za organizację, Pawłowi, dzięki któremu w ogóle dowiedziałem się o tym konkursie oraz Agnieszce, która dzielnie znosiła moje późno wieczorne wojaże z Androidem :)

Monday, November 8, 2010

Circles Mind

Circles Mind is a memory game. It is also a very good brain trainer. You have to tap the last born circle to complete the level. The number of levels depends on your luck, but the maximum is 50. Are you able to remember all 50 circles? No ads during the game!

Changes

Circles Mind v1.1:
- animation added in the main menu
- new menu system support

- first version

Wednesday, November 3, 2010

Let's play the game

New version of the kasur(dev) blog is now up and running. So, welcome to my blog :) You will find here (mainly) some information about my games for the Android platform. All games are developed with my own kasuroid game framework, which is still under development itself. I have just started so don't expect that I will create 3d games with fancy graphics and other great staff in the next week or two. Be patient ;) You have a chance to track the Android games development from scratch. Sounds interesting.. at least for me ;)

I think I will also try to describe some parts of the kasuroid (somewhere in the future :)) in more details. However if you know Polish you may read already published posts about kasuroid. I hope that you will enjoy this blog and... the games! :)

Monday, October 25, 2010

Zmiany

Zastanawiałem się ostatnio co dalej zrobić z blogiem po zakończeniu konkursu (czas ten nieubłaganie nadchodzi). Napisałem już kilka postów i szkoda byłoby je zwyczajnie wyrzucić do kosza. Tym bardziej, że Android jako platforma daje ogromne możliwości jeśli chodzi o reklamę i rozprzestrzenianie swoich produkcji. Pozwala także (przy odrobinie szczęścia) odnieść  pewne korzyści materialne.

Jako, że polubiłem pisanie prostych aplikacji / gier na Androida (i jeszcze przez jakiś czas chcę się w to pobawić) postanowiłem, że ów bloga będę wykorzystywał właśnie jako kanał dystrybucji informacji o bieżących wydaniach, aktualizacjach itd..

Za tym posunięciem kryje się też pewien chytry plan, którego pewnie niektórzy już się domyślają. Chcę sprawdzić w jaki sposób użytkownicy odbierają to co robię (Android market świetnie nadaje się do tego celu).  Wystawiam więc swoje produkcje do oceny ;) Albo trafię w gusta, albo nie. Przy okazji chcę sprawdzić kilka teorii marketingowo biznesowych. Niektóre z nich już się sprawdziły, pozostałe czekają na wdrożenia. Tak czy inaczej zabawa będzie przednia :)

Aby tego dokonać i dotrzeć do szerszego grona użytkowników konieczna jest jednak zmiana języka na blogu. Od tej pory większość postów będę pisał po angielsku (mniej lub bardziej poprawnie :)). Struktura bloga też pewnie ulegnie zmianie.

Jak już wspomniałem wcześniej wszystkie gry powstają na bazie kasuroid. Jest on ciągle rozwijany (w miarę zapotrzebowania na nowe ficzery, na razie jednak tylko lokalnie). Konto na code.google jest (i ma być!) ciągle aktywne. Planuję co jakiś czas (na razie bliżej nieokreślony) aktualizować źródła. Kodu publikowanych gier / aplikacji na razie nie zamierzam upubliczniać. W przyszłości jednak i tego posunięcia nie wykluczam.

Dla rozluźnienia powstanie też pewnie kilka tutoriali (postaram się do nich trochę bardziej przyłożyć) na temat stosowanych przeze mnie rozwiązań. Na razie jednak ich pisanie nie ma najwyższego priorytetu.

Zapowiada się interesująco :)

Monday, October 18, 2010

Witaj świecie!

Uff, nie było mnie tutaj przez jakiś czas. Nie znaczy to jednak, że się obijałem (no, może trochę :)). Otóż postanowiłem skupić się na czymś co mnie odrobinę bardziej bawi i interesuje. Poza tym pisanie (dobrych!) postów pochłania sporo czasu, a tego ostatnio mocno mi brakuje.

Jaki jest więc efekt mojej absencji na blogu? Ano stwierdziłem, że napiszę sobie gierkę i umieszczę ją w Android markecie, aby wybadać "rynek" ;)

Muszę przyznać, że google zrobił kawał dobrej roboty udostępniając w naprawdę bardzo przyjazny sposób swój market. Co trzeba zrobić, aby móc rejestrować swoje aplikacje?

1. Zarejestrować się (jednorazowo!) jako developer uiszczając $25.
2. Napisać aplikację i wrzucić ją do marketu :)

I tyle. Nie ma żadnej certyfikacji ze strony googla (co powoduje jednocześnie zalew marketu często niezbyt użytecznymi aplikacjami - to jest jednak temat mocno dyskusyjny i nie będę jego podejmował).

Niestety z Polski nie można sprzedawać swoich aplikacji (sprawa w toku). Można jednak udostępniać je za darmo, co też i ja uczyniłem.

Udostępnianie aplikacji za darmo można zrekompensować sobie umieszczając reklamy w swojej grze. Również spróbowałem tego rozwiązania (niestety nie bez bólu, ale w końcu się udało - może opiszę to później).

A oto link Nuclear Balls, gdzie można podejrzeć statystyki gry. Gra jest bardzo prosta. Aby przejść poziom należy zniszczyć wszystkie kulki metodą reakcji łańcuchowej. Obecnie mam już opracowaną kolejną wersję (1.1) gry. Możliwe, że zamieszczę ją jeszcze dzisiaj w markecie. Postaram się także wrzucić filmik z gameplaya.

Btw. Nuclear Balls powstała w oparciu o źródła kasuroid. W planie kolejne tytuły :)

Wednesday, October 6, 2010

Trochę zabawy

Wpis ten będzie stosunkowo krótki. Jakoś nie mam weny do pisania dzisiaj :P Dodałem dwa nowe testy: "Stars" oraz "Andro Stars". Narodziły się one podczas rozmyślań nad Rendererem oraz TextureManagerem.
Zmieniłem także odrobinę główną pętlę programu, aby zapewnić bardziej deterministyczne aktualizacje fizyki gry. Szerszy opis na ten temat można znaleźć np. tu: http://www.koonsolo.com/news/dewitters-gameloop/.

Obecnie jest mały bajzel we wspomnianych testach (kodziłem dość szybko i spontanicznie ;P). Jak znajdę dłuższą chwilę to posprzątam i wydzielę generyczne części.

Czas na filmiki. Prezentują one stary, poczciwy efekt przelatywania przez kosmos.

 

 W lewym górnym rogu ekranu dostępne jest proste menu, które umożliwia zatrzymanie (pauza) i wznowienie animacji. Nie mogło się oczywiście obejść bez wersji androidowej:

Sunday, October 3, 2010

Kapryśny Android 2

Może nie tyle kapryśny android co kapryśny proces inicjalizacji całego frameworka. Okazało się, że przez kilka/kilkanaście pierwszych przebiegów runSlice (głównej metody Corea) Renderer rzuca wyjątkiem, tj. uchwyt do ekranu (lub płótna - Canvas - jak kto woli) jest nullem. Problem uchował się do dzisiaj, gdyż podczas rysowania obiektów dodawałem zabezpieczenie tj. "if canvas != null then render()". Kolejne zmiany w kodzie ujawniły robaka :P Poniżej o co tak naprawdę chodzi :)

Jak już wspomniałem w jednym ze wcześniejszym wpisów cały proces inicjalizacji kasuroid przeniosłem z view do głównego activity. Przypomnę, że kasuroid w trakcie startowania uruchamia wątek odpowiedzialny za aktualizację i rysowanie gry. Renderer aby spełniać swe zadanie musi wiedzieć gdzie rysować scenę. Ekran (Canvas) tworzony jest podczas inicjalizowania view głównego activity. Problem pojawia się w momencie, kiedy którykolwiek z elementów kasuroid zaczyna odwoływać się do ekranu gdy ten jeszcze nie został utworzony.

Oczywiście najprostszym sposobem jest wstawienie wspomnianego zabezpieczenia. Jednak jest to tylko ukrycie błędu w kodzie. Innym (lepszym) rozwiązaniem jest rozpoczęcie rysowania dopiero gdy ekran został poprawnie utworzony. Z pomocą przychodzą callbacki SurfaceHolder, które wołane są odpowiednio podczas tworzenia, zmiany oraz niszczenia ekranu. Teraz wystarczy we wspomnianych callbackach powiadomić Core o stanie ekranu. Aby to umożliwić rozszerzyłem interfejs Corea o metody:
  • screenCreated - wołana podczas tworzenia ekranu
  • screenChanged - wołana gdy ekran zmienia swoje parametry (np. podczas obrotu urządzenia)
  • screenDestroyed - wołana gdy ekran został właśnie zniszczony (np. podczas zamykania aplikacji)
Metody te ustawiają jedynie (przynajmniej na razie) zmienną mScreenExists na true (screenCreated, screenChanged) lub na false (screenDestroyed)

A tak wyglądają callbacki:
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
    Core.getInstance().screenChanged(holder, width, height);
    Debug.inf(getClass().getName(), "surfaceChanged");
}

@Override
public void surfaceCreated(SurfaceHolder holder) 
{
    Core.getInstance().screenCreated(holder);
    Debug.inf(getClass().getName(), "surfaceCreated");
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) 
{
    Core.getInstance().screenDestroyed(holder);
    Debug.inf(getClass().getName(), "surfaceDestroyed");
}
W zależności od ustawienia zmiennej mScreenExists w Core, runSlice będzie wykonany lub nie. W zasadzie mam pomysł jak inaczej mógłbym to rozwiązać, ale na razie zostawiam tak jak jest (na refaktoryzację przyjdzie jeszcze czas).

Friday, October 1, 2010

Licznik ramek

Skoro temat timerów jest jeszcze całkiem świeży jest to doskonały czas na napisanie licznika ramek, czyli.. "FPS countera" :)

Zastanawiałem się czy nie zrobić fpsa na bazie klasy Timer, ale ostatecznie stwierdziłem, że kasuroid będzie potrzebował co najwyżej jednego takiego licznika. Nie ma więc sensu tworzyć obiekt klasy Timer. Jest za to sens stworzenia statycznej klasy Fps, zliczającej poszczególne ramki, a następnie wyliczającej liczbę ramek na sekundę.

W klasie Fps wyróżnić można następujące metody:
public static void reset()
{
    mLastTime = 0;
    mCurrentTime = 0;
    mFrames = 0;
    mElapsedTime = 0;
    mFps = 0;
}
Jak nazwa wskazuje resetuje ona wszystkie zmienne potrzebne do wyliczenia fps.
public static void update()
{
    mFrames++;
    mCurrentTime = System.currentTimeMillis();
    mElapsedTime = mCurrentTime - mLastTime;
    if (mElapsedTime >= (long)mFrameLength)
    {
        mFps = (int)((float)mFrames * mFrameLength / (float)mElapsedTime);
        mFrames = 0;
        mLastTime = mCurrentTime;
    }
}
Aktualizuje czasy poszczególnych ramek oraz oblicza aktualne fps.
public static int get()
{
    return mFps;
}
Zwraca (w formacie całkowitym) liczbę ramek na sekundę.

Metodę reset woła Core podczas inicjalizacji. Aby fps był poprawnie liczony Fps.update() musi być wywoływany w każdym przebiegu głównej pętli tj. w runSlice. Zatem, runSlice wygląda teraz tak:
protected int runSlice()
{
    int ret = RetCode.SUCCESS;

    try
    {
        synchronized (mLock) 
        {
            mRenderer.lock();

            // Update all active timers.
            ret = mTimerManager.updateAll();
            if (ret != RetCode.SUCCESS)
            {
                Debug.err(getClass().getName(), "Problem with updating timers!");
                mRenderer.unlock();
                return ret;
            }

            GameState currentState = null;
            if (mGameStatesStack.isEmpty() == false)
            {
                currentState = mGameStatesStack.peek();
                ret = currentState.update();
                if (ret != RetCode.SUCCESS)
                {
                    Debug.err(getClass().getName(), "Problem with updating the state!");
                    mRenderer.unlock();
                    return ret;
                }

                ret = currentState.render();
                if (ret != RetCode.SUCCESS)
                {
                    Debug.err(getClass().getName(), "Problem with rendering the state!");
                    mRenderer.unlock();
                    return ret;
                }
            }

            Fps.update();
        }
    }
    finally 
    {
        // An exception occured, but try to unlock the canvas.
        mRenderer.unlock();
    }

    return ret;
}
runSlice zmienia się wraz z rozwojem kasuroid. Aby zademonstrować działanie Fpsa zaktualizowałem także test "Two timers" o wyświetlanie aktualnego fpsa. Warto pobawić się "sleepTime" Corea i zobaczyć jak zmienia sie fps.
kasuroid zaczyna w końcu raczkować :) Do następnego!

Monday, September 27, 2010

Czas goni nas (2)

Zgodnie z zapowiedzią czas na TimerManagera. Jak sama nazwa na to wskazuje, umożliwia on zarządzanie timerami utworzonymi w kasuroid. Główną metodą jest create():
public Timer create()
{
    Timer timer = new Timer();
    mTimers.add(timer);

    return timer;
}
Tworzy ona obiekt Timer i dodaje go do wektora mTimers (przechowuje wszystkie referencje do timerow utworzonych w TimerManagerze). Istnieje także możliwość usunięcia tak utworzonego timera poprzez zawołanie metody remove:
public int remove(int id)
{
    Timer timer = null;
    Enumeration e = mTimers.elements();
    int i = 0;
    while (e.hasMoreElements())
    {
        timer = e.nextElement();
        if (timer.getId() == id)
        {
            Debug.inf(getClass().getName(), "Timer with id " + id + " found. Removing.");
            mTimers.remove(i);
            return RetCode.SUCCESS;
        }
        i++;
    }

    return RetCode.BAD_PARAM;
}
Oczywiście nic nie stoi na przeszkodzie, aby tworzyć timery samodzielnie, bez udziału TimerManagera. Należy wtedy jednak pamiętać, aby w każdej iteracji głównej pętli Corea zawołać metodę update w celu  zaktualizowania czasu timera.
Każdy utworzony timer identyfikowany jest poprzez unikalne id. TimerManager udostępnia interfejs do sterowania timerami właśnie poprzez id. Są to (znane już metody z klasy Timer):
  • start(int id)
  • stop(int id)
  • pause(int id)
  • resume(int id)
Dodatkowo, interfejs TimerManagera został rozszerzony o dodatkowe metody do zarządzania wszystkimi timerami, a mianowicie:
  • startAll()
  • stopAll()
  • pauseAll()
  • resumeAll()
  • updateAll()
Myślę, że wszystko jest jasne. Aby zademonstrować działanie TimerManagera utworzyłem test Timers. Jest to jedno activity (TestCaseTimersActivity) z jednym stanem (TestCaseTimersState). W teście tworzone są automatycznie dwa timery (timer1 oraz timer2). Użytkownik ma możliwość sterowania timerami poprzez przyciski (mocno symboliczne - na UI przyjdzie jeszcze czas :P) umieszczone na głównym ekranie. A tak test Timers wygląda w praktyce:

Przyciski niebieskie "wołają" odpowiednio (od lewej) startAll, stopAll, pauseAll, resumeAll. Przyciski szare sterują timerem 1, natomiast przyciski różowe sterują timerem 2. W centralnej części wyświetlane są aktualne sekundy poszczególnych timerów. Polecam zassanie źródeł i sprawdzenia timerów w praktyce :)

Wednesday, September 22, 2010

Czas goni nas (1)

Załóżmy, że czas wyrenderowania ramki to 100ms na sprzęcie A. Na sprzęcie B ta sama ramka renderowana jest w ciągu 20ms. Jak łatwo policzyć na sprzęcie A będzie renderowanych 10 klatek na sekundę. Na sprzęcie B będzie to już 50 klatek na sekundę. Jeśli teraz będziemy aktualizować pozycję obiektu o 10 pikseli w każdej ramce to po sekundzie obiekt przebędzie drogę:
  • na sprzęcie A: 10 * 10 pikseli = 100 pikseli
  • na sprzęcie B: 50 * 10 pikseli = 500 pikseli
Jak widać na szybszej maszynie B obiekt przebył znacznie większą drogę niż na maszynie A. Aby wyeliminować to zjawisko animacje obiektów należy opierać właśnie o czas. Wtedy prędkość poruszania się obiektu może być wyrażona w pikselach / sekundę (a nie jak w przykładzie piksele / klatkę), zatem:
  • na sprzęcie A: 1 sekunda * 10 pikseli / sekunda = 10 pikseli / sekunda
  • na sprzęcie B: 1 sekunda * 10 pikseli / sekunda = 10 pikseli / sekunda
Każdy poważny framework (a takim przecież ma być kasuroid :P) powinien posiadać możliwość zarządzania czasem. W kasuroid za czas odpowiedzialna jest klasa Timer. Udostępnia ona podstawowy interfejs do sterowania czasem:
public class Timer
{
    public int start() {}

    public int stop() {}

    public int pause() {}

    public int resume() {}

    public int update() {}
}
Wszelkie obliczenia odbywają się w metodzie update():
public int update()
{
    if ((isStarted() == true) &&
        (isPaused() == false))
    {
        mLastFrame = mCurrentFrame;
        mCurrentFrame = System.currentTimeMillis();
        mDelta = mCurrentFrame - mLastFrame;
        mTimeElapsed += mDelta;
        mDeltaSecs = (float)mDelta / mTimerFrameLength;
    }

    return RetCode.SUCCESS;
}
Pomiar czasu opiera się na różnicy czasów dwóch kolejnych ramek, więc potrzebne jest zainicjalizowanie mLastFrame oraz mCurrentFrame podczas startowania timera:
public int start()
{
    if (isStarted() == false)
    {
        mCurrentFrame = mLastFrame = System.currentTimeMillis();
        mTimeElapsed = 0;
        mDelta = 0;
        mDeltaSecs = 0.0f;

        Debug.inf(getClass().getName(), "Timer with id: " + mId + " started.");
        return RetCode.SUCCESS;
    }
    else
    {
        Debug.warn(getClass().getName(), "Timer already started!");
        return RetCode.SUCCESS;
    }
}
Warto zauważyć, że w update() obliczam od razu liczbę sekund trwania jednej ramki. Doszedłem do wniosku, że dużo obiektów będzie bazowało swoje animacje właśnie na podstawie tej wartości. Dlatego też wystarczy policzyć ją raz per ramka. Każdy obiekt może następnie skorzystać z poniższej metody do pobrania czasu (w sekundach):
public float getTimeDeltaSecs()
{
    return mDeltaSecs;
}
.. i obliczyć na jego podstawie kolejny krok animacji.

Timer liczy także czas jaki upłynął od czasu wywołania metody start() (zmienna mTimeElapsed).

I to chyba tyle. W następnej części przedstawie TimerManagera.

Sunday, September 19, 2010

Kapryśny Android

Sprawa dotyczy zachowania się activity podczas obrotu. Aby obrócić ekran emulatora należy użyć CTRL+F12. Wspomniana kombinacja klawiszy obraca ekran o 90 stopni. Możemy w ten sposób przetestować jak zachowa się nasza aplikacja podczas obracania rzeczywistego urządzenia. Co się okazuje (ja byłem całkiem mocno zaskoczony :)) podczas takiego obrotu niszczone jest bieżące activity, a następnie tworzone nowe. Niezła niespodzianka, prawda? ;)

Problem okazał się całkiem poważny, gdyż podczas onCreate oraz onDestroy (a dokładniej podczas tworzenia / niszczenia powierzchni w KasuroidView), kasuroid jest odpowiednio inicjalizowany / kończy pracę. Okazało się, że startowanie Corea w KasuroidView nie jest dobrym pomysłem (szczerze mówiąc od samego początku takie rozwiązanie mi się nie podobało i prędzej czy później chciałem to zmienić).

Pierwszym podejściem do rozwiązania problemu było zablokowanie przejścia do trybu landscape (obrót o 90 stopni w stosunku do trybu portrait, w którym odpalany jest emulator). Aby wymusić dany tryb wyświetlania aplikacji (bez możliwości jego zmiany) należy w manifeście dla odpowiedniego activity dodać android:screenOrientation="portrait". Niestety (jak można się domyślić) nie rozwiązało to problemu w 100% :) Ekran owszem nie jest skalowany do trybu landscape przy obrocie, ale activity nadal jest niszczone. Na dodatek chciałbym, żeby kasuroid jednak wspierał tryb landscape.

Chwila szukania / czytania API i.. jest światełko w tunelu. Okazuje się, że na activity tuż przed onDestroy wołana jest metoda onSaveInstanceState. Umożliwia ona zapisanie stanu aplikacji, który następnie może zostać odtworzony w onCreate

Na teraz nie bardzo sobie wyobrażam jakie dane mógłbym zapisywać (w przypadku gry mogą to być jednak parametry leveli itd.), ale to co mogę zrobić w takiej sytuacji to zabezpieczyć Core przed restartowaniem. Warto wspomnieć, że Core jest singletonem. Co za tym idzie obiekt Corea przechowywany jest w zmiennej statycznej, a te nie są niszczone, jeśli activity jest przenoszone do hmm.. tła (minimializowanie) lub np. jak w tym przypadku, następuje obrót ekranu.

Przede wszystkim rozszerzyłem interfejs Corea o możliwość zatrzymywania i włączania głównego wątku (metody stop() oraz start()). Wątek nie robi nic poza wołaniem runSlice, więc można spokojnie nim manipulować ;). Następnie cały proces inicjalizacji / przerywania pracy / startowania Corea przeniosłem do KasuroidActivity.

A w jaki sposób sprawdzić czy activity jest jedynie maksymalizowane czy też tworzone całkiem od nowa? Hmm.. zastosowałem trochę "hardkorowe" podejście i pewnie zostanę mocno skarcony przez znawców Androida :P. Zauważyłem, że jeśli activity wraca do pierwszego planu to parametr w onCreate nie jest zerowy (lub nie jest nullem). Wystarczy zatem sprawdzić uchwyt do Bundle przekazanego jako parametr i wtedy albo uaktualniamy jedynie powierzchnię Renderera albo inicjalizujemy Corea. Następnie wystarczy już tylko wystartować wątek Corea.

W związku z powyższą zmianą logiki startu activity, do KasuroidActivity dodałem dwie metody onInit oraz onTerm. onInit wołana jest w przypadku kiedy tworzone jest nowe activity, natomiast onTerm, kiedy activity permanentnie jest niszczone.

W przypadku wspomnianego w poprzednim wpisie testu stanów gry, inicjalizacja pierwszego stanu została przeniesiona z onCreate do onInit (rozwiązuje to problem zmiany stanu, w przypadku gdy activity przeszło do tła przy aktywnym stanie drugim).

Źródła uaktualnione, więc zachęcam do podejrzenia kodu.

Btw. cykl życia androidowego activity jest dość ciekawy i bardzo możliwe, że w trakcie rozwijania projektu KasuroidActivity trochę się pozmienia :) 

Jeszcze takie spostrzeżenie. Praca nad kasuroid w całkiem dużym stopniu przypomina programowanie ekstremalne ;)

Do następnego!

Thursday, September 16, 2010

Stany (nie do końca zjednoczone)

Środowisko do testów właściwie gotowe, więc można dłubać dalej. Poniżej postaram się przybliżyć odrobinę o co chodzi z tymi stanami gry i dlaczego warto stosować takie podejście.

Bardzo popularne w grach są różnego rodzaju intra. Np. najprostsze to wyświetlenie przez jakiś czas logo producenta gry. Następnie pojawia się na ogół główne menu gry. Po wybraniu "start/nowa gra" rozpoczyna się gra. To co wymieniłem to właśnie stany gry. Czy można się bez nich obejść, tzn. bez definiowania oddzielnych klas do ich obsługi? Oczywiście, że można, ale czy nie lepiej mieć porządek w kodzie i wszytko ładnie logicznie podzielone?

Jak to wygląda w praktyce?

W kasuroid zdefiniowałem klasę bazową dla stanów gry tj. GameState (tak, State wrócił do pierwotnej nazwy). Posiada ona następujące metody:
  • init()
  • term()
  • pause()
  • resume()
  • update()
  • render()
Powyższe metody oznaczone są jako final i nie mogą być przeciążone przez klasy pochodne. Zdecydowałem się na takie podejście, aby zapewnić wewnętrzna kontrolę stanu. Np. wywołanie update, gdy obiekt nie jest zainicjalizowany wygeneruje błąd. Można jednak przeciążyć metody, które będą wołane w odpowiednich stanach tj.:
  • onInit()
  • onTerm()
  • onPause()
  • onResume()
  • onUpdate()
  • onRender()
Zarządzanie stanami gry zrealizowałem w oparciu o stos (mGameStatesStack w Core). Core posiada interfejs do zarządzania stanami, a mianowicie:
  • changeState(GameState gameState) - czyści stos, po czym wrzuca na niego gameState
  • pushState(GameState gameState) - pauzuje aktualnie znajdujący się na szczycie stosu stan i dodaje gameState
  • popState() - zdejmuje ze stosu aktualny stan i włącza (resume) stan następny
Jest to chyba najprostszy sposób realizacji zarządzania stanami gry. Można jednak przy jego pomocy realizować całkiem ciekawe przejścia w grze (co też zamierzam w odpowiednim czasie pokazać).

Ok, czas na pierwszy oficjalny test :) Dodałem nowy pakiet do projektu: com.kasuroid.test.cases. Do tego pakietu mam zamiar wrzucać wszystkie testowe activity wraz z potrzebnymi klasami. Aby mieć w miarę porządek i jako, że pojedynczy test może składać się z wielu plików, pakiet com.kasuroid.test.cases będę dzielić na drobne "podpakiety". Pierwszy podpakiet to GameStates. Stworzyłem grupę "Game states" i zdefiniowałem TestCaseGameStateActivity oraz dwa "stany gry": TestCaseGameStateState1, TestCaseGameStateState2. Aby zainicjować pierwszy stan wystarczy wywołać change state w activity:
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        Core.getInstance().changeState(new TestCaseGameStateState1());
    } 
Gdy jesteśmy w stanie 1 (aktywny TestCaseGameStateState1) i naciśniemy dowolny klawisz to przejdziemy do stanu 2 (TestCaseGameStateState2). Jeśli zostanie naciśnięty klawisz w stanie 2 to nastąpi powrót do stanu 1. Wyjść z programu można w dowolnym momencie klikając na ekran.

Stan 1:
Stan 2:
Kliknięcie na ekran powoduje wyjście z testu:
Aby lepiej zobaczyć jak zachowują się poszczególne elementy kasuroid proponuję uruchomić projekt i podejrzeć logi (próbowałem przekleić chociaż część logów, ale poszczególne wiersze nie mieszczą się w jednej linii, a mi nie bardzo chce się ich ręcznie ciąć :P).

Dobrej nocy!

Tuesday, September 14, 2010

Kasuroid tests

Dodałem do projektu możliwość odpalania testów. No, powiedzmy, że nie są to testy w pełni funkcjonalne (niestety nie mam czasu, żeby bardziej bawić się w testowanie), ze sprawdzaniem statusu/czasu wykonania itd.. Możliwe jest jednak uruchamianie pojedynczych activity (z racji tego, że większość testów i tak będę przeprowadzał wizualnie jest to całkiem dobre rozwiązanie). Na dodatek activity można pogrupować, np. podczas prac nad Rendererem wszystkie ew. test casey będą lądować do grupy Renderer.

Aby wszystko powyższe było możliwe dodałem następujące klasy:

KasuroidTestActivity - jest to w zasadzie proste UI (w oparciu o ListView) do zarządzania testami. Tworzy obiekt TestManager. W tej klasie tworzona jest również podczas inicjalizacji (przynajmniej na razie) struktura testów, czyli definiowane są grupy i przypisywane do nich odpowiednie testy. Lista grup / testów przełączana jest dynamicznie.

TestManager - w skrócie zarządza testami. Jest odpowiedzialny za uruchamianie "testów" oraz (może w przyszłości..) prowadzenie statystyk itd..

TestCaseGroup - trzyma listę testów. Każda grupa (podobnie jak obiekty klasy TestCase) identyfikowane są poprzez nazwę oraz unikalny (w skali wszystkich testów) identyfikator.

TestCase - to co najważniejsze w tej klasie to zmienna trzymająca hmm.. znacznik? klasy activity, która ma zostać uruchomiona. Początkowo TestCase dziedziczył po KasuroidActivity, jednak problemem okazało się uruchamianie takiego activity (konstruktor uruchamianego activity musi (?) być bezparametrowy, co w naszym przypadku się nie sprawdza, gdyż TestCase jako parametr przy tworzeniu bierze nazwę testu). Poza tym w TestCaseGroup przechowywane są utworzone już obiekty typu TestCase, więc tworzenie ich na nowo (tym razem przy użyciu bezparametrowego konstruktora) jest odrobinę bez sensu.

W jaki sposób zdefiniować strukturę testów? Nic prostszego:
    TestCaseGroup group = new TestCaseGroup("Group1");
    group.addTestCase(new TestCase("Test11", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test12", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test13", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test14", KasuroidActivity.class));
    mTestManager.addTestGroup(group);

    group = new TestCaseGroup("Group2");
    group.addTestCase(new TestCase("Test21", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test22", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test23", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test24", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test25", KasuroidActivity.class));
    group.addTestCase(new TestCase("Test26", KasuroidActivity.class));
    mTestManager.addTestGroup(group);
Jak widać powyżej utworzone zostały dwie grupy testów: Group1 oraz Group2. Do każdej z grup przynależy również kilka testów. Każdy test odpala KasuroidActivity (na dzień dzisiejszy nie ma za wiele innych klas testowych :) ). Oczywiście powyższa struktura testów jest tylko testem.

I tak po odpaleniu KasuroidTestActivity ukazuje się lista grup testów:
Po wybraniu "Group1" wyświetlona zostaje lista testów przypisanych do "Group1":
Aby przejść do widoku grup testów należy wcisnąć "Back button". Po wybraniu testu z listy zostanie uruchomione przypisane activity.

Jeszcze może kilka słów na temat czemu takie akurat rozwiązanie. Głównie dlatego, żeby móc w prosty sposób śledzić postęp prac nad kasuroid oraz z.. lenistwa. To czego najbardziej nie lubię to tworzenia nowych projektów i ustawiania wszystkich ścieżek itd.. (a tak musiałbym robić jeśli chciałbym mieć sprawdzane kolejne funkcjonalności frameworka). W powyższym rozwiązaniu jedyne co będę musiał zrobić to utworzyć (poprzez skopiowanie już istniejącego) nowe activity (swoją drogą dziedziczące po KasuroidActivity) i dodać do odpowiedniej grupy testów. Dodatkową zaletą takiego rozwiązania jest fakt, że podczas odpalania activity inicjalizowany jest cały kasuroid framework. Symulowany jest więc normalny cykl życia przyszłych gier w oparciu o kasuroid

Wszystko w jednym miejscu, w jednym projekcie. Cudnie!

Sunday, September 12, 2010

quit!

Jak to często bywa koncepcja klaruje się w trakcie samej pracy (w tym przypadku nie jest inaczej :P). W trakcie prac nad małym środowiskiem testowym okazało się, że nie ma opcji wyjścia z aplikacji :) Głównie dlatego, że beż czarowania nie ma sposobu na zatrzymanie wątku Core przy użyciu samego Core. Jako, że całe sterowanie ma się odbywać właśnie przez Core musi być to możliwe. Dlatego też, tak jak przewidywałem we wcześniejszym wpisie, KasuroidThread staje się wewnętrzna klasą Core. Jest to o tyle wygodne, że teraz mamy pełną kontrolę nad sposobem odpalania Core. Na dodatek zmniejsza się liczba klas wejściowych do frameworka (btw. w planie jest aby jedynym punktem wejścia do kasuroid była klasa KasuroidActivity). Interfejs Core został rozszerzony o metody start() oraz stop() oraz dodałem kontrolę stanów, w których Core może się znajdować.

Aby zakończyć pracę frameworka należy wywołać metodę quit():
    public void quit()
    {
        Activity act = (Activity)mContext;
        act.finish();
    }
mContext wskazuje na activity, w którym utworzony jest Core. Jako, że w Androidzie aplikacja jest odpalana poprzez Activity, zamknięcie samej aplikacji musi się odbyć również poprzez zamknięcie Activity. Można to zrobić np. poprzez zawołanie metody finish() na danym activity. finish() zamyka kolejne widoki i (pośrednio) w KasuroidView również Core. Poniżej znajduje się fragment LogCat z inicjalizacji oraz kończenia aplikacji:

Starting activity: Intent { cmp=com.kasuroid/.KasuroidActivity } 
com.kasuroid.core.Core:Core()
com.kasuroid.core.Core:init()
com.kasuroid.core.TimerManager:TimerManager initialized
com.kasuroid.core.SceneManager:SceneManager initialized
com.kasuroid.core.ResourceManager:ResourceManager initialized
com.kasuroid.core.Renderer:Renderer initialized
com.kasuroid.KasuroidActivity:Debug output
KasuroidView:surfaceCreated
com.kasuroid.core.Core:start()
Displayed activity com.kasuroid/.KasuroidActivity: 279 ms (total 279 ms)
com.kasuroid.core.Core:Touch event, going to close app!
com.kasuroid.core.Core:stop()
com.kasuroid.core.Core:term()
com.kasuroid.core.TimerManager:TimerManager terminated
com.kasuroid.core.SceneManager:SceneManager terminated
com.kasuroid.core.ResourceManager:ResourceManager terminated
com.kasuroid.core.Renderer:Renderer terminated
com.kasuroid.KasuroidActivity:onDestroy

Jak widać wszystkie moduły są już ładowane oraz zwalniane. Aha, com.android.kasuroid ewoluował (nie bez bólu..) do com.kasuroid. Zmieniłem pakiet główny ponieważ kasuroid jako taki nie jest częścią pakietu android a całkiem oddzielnym komponentem. 
Ponieważ Core odpalany jest w oddzielnym wątku, cały dostęp (a przynajmniej do najważniejszych składowych) musi być synchronizowany (zmienna mLock w Core). 
Okazało się także, że CoreThread potrafi wywłaszczyć wszystkie zasoby i nie jest możliwa komunikacja z użytkownikiem / systemem. Aby rozwiązać ten problem musiałem uśpić na chwilę wątek i oddać trochę CPU systemowi (dodanie sleep w run() wątku). Dodatkową zaletą uśpienia wątku (oprócz możliwości komunikacji z systemem i otrzymywania różnych eventów) jest oszczędzanie baterii - CPU nie działa wtedy na 100%.

W następnym wpisie przedstawię więcej szczegółów na temat testowania frameworka.

Wednesday, September 8, 2010

What a terrible failure

Android SDK dostarcza możliwość logowania różnych sytuacji zaistniałych w kodzie za pomocą klasy Log. Zrobiłem małego wrapera i w kasuroid logowanie jest możliwe za pomocą klasy Debug. A dlaczego tak? A po to, żeby za każdym razem nie wpisywać chociażby "taga" oraz mieć większą kontrolę nad formatem wypluwanych danych (i np. w prosty sposób zrobić logowanie do pliku, na serwer czy gdzie tam komu się podoba). Domyślnie logi można podejrzeć w LogCat:


Btw. klasa "Log" jest przykładem na to, jakim poczuciem humoru obdarzeni są programiści gugla :) Jedną z metod tej klasy jest... "wtf" :) Nie, nie. Nie jest to "What The Fuck?!", ale prawie równie wymowne "What a Terrible Failure" :)

Z pozostałych rzeczy.. Główne activity zmieniło nazwę na KasuroidActivity. KasuroidThread został również rozbudowany. Jest to klasa wątku, w którym odpalany jest core frameworka. Możliwe, że później KasuroidThread zostanie wchłonięty przez Core, ale na razie zostawiam to tak jak jest.

Aby wystartować framework należy w zasadzie utworzyć obiekt klasy KasuroidThread i wywołać metode proceed. Metoda ta inicjalizuje (poprzez Core) wszystkie moduły:
  1 public int proceed()
  2 {
  3     int ret = RetCode.SUCCESS;
  4         
  5     ret = Core.getInstance().init(surface, context);
  6     if (ret != RetCode.SUCCESS)
  7     {
  8         Debug.err(this.getClass().getName(), "Core not initialized!");
  9         return ret;
 10     }
 11 
 12     setRunning(true);
 13     start();
 14 
 15     return RetCode.SUCCESS;
 16 }
Aby zakończyć pracę należy wywołać metodę shutDown:
  1 public int shutDown()
  2 {
  3     // Shut down the core thread.
  4     boolean retry = true;
  5     setRunning(false);
  6     while (retry) 
  7     {
  8         try 
  9         {
 10             this.join();
 11             retry = false;
 12         } 
 13         catch (InterruptedException e) 
 14         {
 15             Debug.err(this.getClass().getName(), e.toString());
 16             return RetCode.FAILURE;
 17         }
 18     }
 19 
 20     int ret = Core.getInstance().term();
 21     if (ret != RetCode.SUCCESS)
 22     {
 23         Debug.err(this.getClass().getName(), "Core wasn't properly terminated!");
 24     }
 25 
 26     return ret;
 27 }
Główną pętlę całego frameworka zawiera metoda run wątku:
  1 public void run() 
  2 {
  3     while (isRunning) 
  4     {
  5         Core.getInstance().runSlice();
  6     }
  7 }

Jak widać nie robi ona praktycznie nic oprócz wołania metody runSlice z Corea. To właśnie w runSlice będą się dziać wszystkie magiczne aktualizacje sceny, fizyki i rysowanie po ekranie.

Sunday, September 5, 2010

KasuroidView

Czas na zdefiniowanie widoku. Do com.android.kasuroid wrzuciłem KasuroidView. Definicja nowego widoku znajduje się w /layout/kasuroid_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <com.android.kasuroid.KasuroidView
        android:id="@+id/id_kasuroid_view"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
</FrameLayout>

LinearLayout zmieniłem na rzecz FrameLayout, gdyż umożliwia on nakładanie na siebie kolejnych widoków (co też ładnie zostało przedstawione tutaj). Może to być użyteczne np. w przypadku, gdy chcemy wyświetlić jakąś dodatkową informację bezpośrednio na ekran. Warto wspomnieć o android:id. Jest to identyfikator danego elementu, w tym przypadku "id_kasuroid_view". KasuroidView dodawany jest do głównego activity poprzez:
  • setContentView(R.layout.kasuroid_layout);
, gdzie "R" jest to automatycznie generowana z aktualnych zasobów klasa ze zdefiniowanymi identyfikatorami. Należy pamiętać, żeby użyć poprawnej klasy identyfikatorów ("R"). A mianowicie:
  • import com.android.kasuroid.R;
Aplikacje mają być pełnoekranowe. W celu ukrycia paska tytułu wystarczy zawołać:
  • requestWindowFeature(Window.FEATURE_NO_TITLE);
Jako, że zdecydowałem się na użycie oddzielnego wątku do rysowania, wymagane jest dziedziczenie po SurfaceView, co z kolei prowadzi do konieczności zaimplementowania kilku funkcji:
  • surfaceChanged
  • surfaceCreated
  • surfaceDestroyed
Wynikiem powyższego jest... pusty, czarny ekran ;):

Pierwsze źródła

Uff.. po dłuższej przerwie (i zasłużonych wakacjach :P) czas zacząć dłubać coś bardziej konkretnego.

Zgodnie z tym co zostało "powiedziane" w poprzednim poście pierwsze źródła wrzuciłem do repozytorium. Do SVNa używam TortoiseSVN, który to bardzo ładnie integruje się z powłoką Windowsa. Strukturę projektu (która zapewne jeszcze się zmieni) każdy może sobie podejrzeć, wiec przedstawię jedynie to co najważniejsze. W projekcie wydzieliłem dwa pakiety: com.android.kasuroid oraz com.android.kasuroid.core. Taki układ wydaje mi się (przynajmniej na razie) najbardziej sensowny. Do *.core będzie leciało wszystko co jest generyczne dla frameworka.

com.android.kasuroid
  •  Kasuroid.java

com.android.kasuroid.core
  • Core.java
  • Module.java
  • Renderer.java
  • ResourceManager.java
  • RetCode.java
  • Scene.java
  • SceneManager.java
  • SoundManager.java
  • State.java
  • Timer.java
  • TimerManager.java

Póki co nie ma sensu wydzielać oddzielnych podkatalogów na poszczególne moduły. Na tym etapie nie przewiduję, że framework będzie kompilowany do biblioteki (aczkolwiek byłoby to bardzo wygodne rozwiązanie w przypadku gotowego API silnika - co prędko jednak się nie zdarzy :P). Każda gra/aplikacja, chcąca korzystać z kasuroid będzie musiała dodać jego źródła do swojego projektu. Jest to chyba najprostsze z możliwych rozwiązań. Wadą jest jednak to, że trzeba się mocno pilnować, żeby w trakcie rozwijania gry nie namieszać w źródłach samego frameworka. W momencie gdy z frameworka nie korzysta żadna aplikacja nie ma to jednak większego znaczenia :)

Aha, GameCore oraz GameState przechrzciłem na Core oraz State. Dodałem również interfejs Module oraz definicje kodów powrotu metod.

Module.java
public interface Module {

    int init();

    int term();

    String getName();
}

Jestem zwolennikiem jawnego inicjalizowania oraz hmm.. kończenia pracy poszczególnych części kodu (w szczególności modułów). Albo zwyczajnie mam stare przyzwyczajenia z C/C++ ;P

RetCode.java
public class RetCode
{
    public static final int SUCCESS = 0;

    public static final int FAILURE = 1;

    public static final int BAD_PARAM = 2;
}

Czyli raczej standard. Nie jest to ostateczna liczba kodów i będzie ona rozszerzana w miarę potrzeb.

Tuesday, August 24, 2010

Podstawy frameworka

W poście tym postaram się podać w końcu trochę więcej konkretów. Głównymi elementami frameworka będą:
  • GameCore
  • GameState
  • SoundManager
  • ResourceManager
  • TimerManager
  • SceneManager
  • AnimationManager
  • InputManager 
  • Renderer

GameCore
Punkt wejścia do frameworka. Odpowiedzialny za inicjowanie wszystkich modułów i scalanie ich ze sobą. Centralna część "systemu".

GameState
Klasa bazowa dla stanów gry.

SoundManager
Jak sama nazwa wskazuje jest to menadżer odpowiedzialny za dźwięki w grze.

ResourceManager
Ładowanie i zarządzanie zasobami w grze (np. ładowanie grafiki).

TimerManager
Zarządza timerami (licznikami czasu) w grze.

SceneManager
Zarządzanie scenami. Teoretycznie ma być możliwość tworzenia i wyświetlania wielu scen (np. animowane menu na animowanym tle).

AnimationManager
Zarządzanie animacjami w grze. 

InputManager
Zdarzenia użytkownika, np. przechwytywanie "tapnięcia" palcem w ekran.

Renderer
Wyświetlanie grafiki.

Póki co czysta teoria. Każdy z modułów zostanie w swoim czasie (zapewne w trakcie implementacji) szerzej opisany i... skorygowany czy w ogóle będzie potrzebny :)

    Thursday, August 19, 2010

    Rysowanie po ekranie

    Środowisko postawione, więc można zacząć przyglądać się bliżej jak działa Android, a dokładniej w jaki sposób można się dobrać do ekranu.

    Rysować (czy też malować) można na dwa sposoby.
    1. Wrzucić rysowanie obiektów do metody onDraw() danego widoku (View). W momencie, kiedy system wykryje potrzebę odmalowania ekranu (np. gdy główne okno dostanie focusa), przejdzie przez cała hierarchie widoków wywołując metody onDraw. Jest to idealne rozwiązanie np. dla rysowania kontrolek, które nie zmieniają zbyt często swojego stanu.
    2. Bezpośrednie malowanie na płótnie (Canvas) z określoną częstotliwością. Od wersji 1 różni się tym, że sami wymuszamy niejako wywołanie funkcji onDraw().
    Z racji tego, że w planie mam zbudowanie super wydajnego frameworka ;) zwycięzcą jest oczywiście podejście drugie.

    Żeby nie było tak prosto, w sposobie 2 również mamy dwa podejścia. Pierwsze to rysowanie w wątku Activity (coś w rodzaju okna aplikacji) np. poprzez założenie timera i wołanie np. co 40ms (aby mieć płynną animację potrzebujemy co najmniej 25 klatek na sekundę) metody invalidate. Drugie podejście to wykorzystanie SurfaceView i renderowanie sceny gry tyle razy na sekundę na ile pozwala urządzenie w specjalnie do tego celu utworzonym wątku. Póki co nie decyduję się jeszcze na konkretne rozwiązanie (myślę jednak, że wykorzystanie drugiego wątku będzie efektywniejsze).

    Tuesday, August 17, 2010

    Środowisko

    Nie będę się za wiele rozpisywał (i kopiował tego co już Google napisał). Wystarczy wejść na stronę http://developer.android.com/sdk/index.html i wykonać wszystkie kroki. Efektem poprawnej instalacji środowiska jest uruchomienie emulatora:
    Niestety nie posiadam (i raczej nie będę posiadał w najbliższym czasie) komórki z Androidem dlatego cały projekt będę rozwijał z użyciem dostarczonego przez Google emulatora. Jako, że chcę, żeby kasuroid działał na jak największej liczbie urządzeń, na platformę bazową wybrałem Android 1.6 (aplikacje stworzone na 1.6 powinny ;) działać na nowszych wersjach platformy).

    Jeszcze kilka słów na temat samego emulatora. Niestety nie jest on wydajny (jak to już bywa z emulatorami). Pożera sporo zasobów i przede wszystkim długo trwa jego pierwsze uruchomienie. Prawdę mówiąc na początku byłem bliski zrezygnowania z projektu. Na Windows XP Pro 32b z procesorem Sempron 2200 i pamięcią 1GB (praktycznie wszystko wyłączone w tle) czas uruchomienia emulatora wynosił ~10 minut! Tak, wiem, stara maszyna, ale do bieżącego "developmentu" w Visual Studio 2005/Eclipse spełnia swoje zadanie w 100%. Na linuxie (Ubuntu 10.04) niestety nie było lepiej. Czas pierwszego uruchomienia można jeszcze przeżyć (nie trzeba restartować emulatora, aby wrzucić nową wersję aplikacji). Niestety czas wrzucenia aplikacji na uruchomiony emulator to ~2 minuty i tego już nie mogłem zaakceptować. Tym bardziej, że w początkowej fazie projektu i bez wiedzy na temat platformy dużo rzeczy trzeba sprawdzać na bieżąco.

    Na drugiej maszynie jest już znacznie lepiej. Windows 7 32b, Intel i5 oraz 3GB pamięci pozwala na normalne użytkowanie emulatora. Czas uruchomienia emulatora skrócił się do 40 sekund a czas wrzucenia aplikacji na emulator (i jej uruchomienie) to zaledwie ~5 sekund. Można więc pracować :)

    Wednesday, August 11, 2010

    kasuroid

    Celem projektu kasuroid jest powstanie frameworka do tworzenia gier na platformę Android. Jako dodatkowy cel, w trakcie trwania konkursu, stawiam przed sobą napisanie gry na bazie owego frameworka.

    Chciałbym od razu zaznaczyć, że na Androida nie napisałem jak do tej pory ani jednej linijki kodu. Jest to dla mnie dziewicza platforma ;).

    Dlaczego właśnie Android?

    Wybór Androida nie jest przypadkowy. Google, oprócz całkiem niezłego mobilnego OSa (nie jest to jednak ich autorski produkt :P), daje nam możliwość wypromowania swoich aplikacji na świat i osiągnięcia z tego tytułu (przy odrobinie szczęścia) również innych korzyści. kasuroid ma mi właśnie uzmysłowić (i kto wie, może także czytelnikom tego bloga) odrobinę inne podejście do tworzenia aplikacji.

    Póki co nie mam pojęcia jak bardzo projekt uda mi się rozwinąć w trakcie trwania konkursu (składa się na to wiele czynników, a głównym jest dostępność mojej osoby i chociażby zbliżający się wyjazd w góry ;)). Staram się mieć luźne podejście do tego projektu i nie narzucać na siebie rygorystycznych terminów ;) W innym przypadku szybko stracę motywację i chęci :P

    W każdym bądź razie pierwszą (konkursową) fazę projektu uznam za ukończoną w momencie kiedy uda się na bazie kasuroid stworzyć kosmiczną strzelankę :) Oczywiście nie wykluczam (a nawet przewiduję) powstanie innych "tytułów" w trakcie rozwijania frameworka. Finałowa gra będzie nosić nazwę "Astro Warrior" :)

    Ps. Tak, zawsze chciałem napisać space shootera :)

    Tuesday, August 10, 2010

    Początek..

    Od pewnego już czasu nosiłem się z ideą publikowania materiałów na temat programowania i generalnie o tym czym się zajmuję w wolnym (lub nie :P) czasie.  Tak się złożyło, że trafiłem na stronę Maćka Aniserowicza oraz na organizowany przez niego konkurs Daj się poznać. Po zastanowieniu stwierdziłem, że jest to idealna okazja, żeby zacząć i zgłosiłem swoje uczestnictwo. Konkurs sam w sobie jest szansą na stworzenie (i przede wszystkim doprowadzenie do końca) swojego projektu, okazją poznania nowych technologii oraz (na co liczę) dobrą zabawą :). Oczywiście dodatkowym motywatorem są nagrody (MSDN Ultimate chyba nikt by nie pogardził.. ja na pewno nie :P).

    Zasady konkursu:
    1. Projekt w dowolnej technologii na dowolnej platformie.
    2. Projekt typu "open source".
    3. Publikowanie postępów w pracy przez co najmniej 10 tygodni (minimum dwa wpisy na tydzień) na swoim blogu.
    I to tyle (a może _aż_ tyle :)).

    W następnym poście postaram się przedstawić wizję mojego projektu o jakże dumnej (i dziwnej :P) nazwie.. "kasuroid" ;).