Monthly archive for November, 2008

Coś się zepsuło

2008-11-28 21:00

No to wykrakałem… Niecały rok temu pozwoliłem sobie ironizować na temat dziwnych przypadków dotykających komputery w Ministerstwie Sprawiedliwości, a teraz przytrafiło mi się coś w pewnym stopniu podobnego. No, może nie do końca – wprawdzie na pierwszy rzut oka mój przenośny komputer nadal wydaje się cały, ale trudno go nazwać działającym. Pewnego razu parę dni temu po prostu bardzo ładnie się zwiesił, zaś po sprzętowym restarcie nie okazuje żadnych znaków życia poza migającą kontrolką zasilania.
Na szczęście istnieją jeszcze serwisy gwarancyjne, które teoretycznie potrafią uporać się z każdą naprawą w średnio dwa tygodnie. Cokolwiek się więc zepsuło (strzelam w przegrzanie karty graficznej lub procesora), powinno zostać naprawione w jakimś rozsądnym czasie. A zanim tak się stanie, muszę przeprosić się z nieco zaniedbanym komputerem stacjonarnym :)

Tags: ,
Author: Xion, posted under Life » 8 comments

Cztery lata WoW-a

2008-11-23 21:01

Właśnie dzisiaj mijają dokładnie cztery lata od premiery pewnej gry, która chyba na długie lata pozostanie wzorcem w swoim gatunku – czyli World of Warcraft produkcji Blizzarda. Całkiem zresztą możliwe, że jest to też najlepiej zarabiająca gra w całej historii komputerowej rozrywki, a z pewnością najbardziej znana i najpopularniejsza (11 milionów graczy) produkcja z rodzaju MMORPG.
Wśród tych wszystkich “najów” tym bardziej zaskakujące jest też to, że granice dalszego wzrostu popularności WoW-a wcale nie wydają się być nie tyle nawet osiągnięte, co wręcz osiągalne w przewidywalnej przyszłości. Wystarczy chociażby spojrzeć na wyniki sprzedaży niedawno wydanego (w zeszłym tygodniu) dodatku Wrath of the Lich King. Nie da się zresztą ukryć, że Blizzard dwoi i się troi, aby wszyscy grający w WoW-a mieli co robić w wirtualnym świecie. Rezultaty bywają czasami zabawne; zalicza się do nich na przykład fakt, że wszystkie dungeony wchodzące w skład wspomnianego dodatku zostały ‘wyczyszczone’ już w… 2 dni od jego wydania. To na pozór nielogiczne, ale takie obniżenie poziomu trudności ma jak najbardziej sens. W końcu gracze casualowi i hardcore‘owi płacą po tyle samo, ale tych pierwszych jest przecież kilka(naście) razy więcej niż drugich. Nic więc dziwnego, że (niemal) cała treść gry zaczyna być kierowana właśnie do nich.

Czy na dłuższą metę (czyli przynajmniej następne cztery lata) okaże się to dobrą strategią, nie sposób przewidzieć. Póki co nic nie wskazuje jednak na to, aby było inaczej. Mimo spadku znaczenia PC-tów i postępu technologicznego, oświetlany per vertex świat Azeroth przetrwa więc pewnie jeszcze bardzo, bardzo długo :)

Tags:
Author: Xion, posted under Games » 10 comments

Moc breakpointów w VS

2008-11-21 20:28

Śledzenie wykonywania programu to świetna metoda na znajdowanie przyczyn wielu błędów. Ale żeby coś sensownie śledzić, to najpierw zwykle trzeba dojść do interesującego fragmentu kodu – to zaś umożliwiają punkty przerwania, czyli po polsku breakpointy ;)
W najbardziej podstawowej wersji działają one niezwykle prosto i zwyczajnie zatrzymują debuger na wskazanej instrukcji. W Visual Studio możemy jednak wykorzystać w sposób znacznie bardziej zaawansowany; wystarczy bowiem – po postawieniu breakpointa kliknąć weń prawym przyciskiem myszy i już ukazuje się nam wielce interesujące menu podręczne. Mamy tam kilka przydatnych opcji, do których należą między innymi:

  • Location – umożliwia precyzyjne określenie, na jakiej instrukcji stawiamy breakpoint. Jest to użyteczne, gdy mamy np. krótką jednolinijkową instrukcję if, a punkt przerwania chcemy ustawić nie na sprawdzaniu jej warunku, lecz w środku jej bloku.
  • Hit Count – wyświetla nam okienko, w którym widać, ile razy breakpoint został “trafiony” od początku sesji debuggera. Możemy też określić tam, aby zatrzymanie następowało tylko przy jakimś określonym przejściu przez breakpoint. Może to być przydatne, jeśli np. debugujemy pętle, o której wiemy, że zaczyna się psuć w okolicach 4867 iteracji – właśnie tyle wciśnięć F5 możemy w ten sposób oszczędzić :)
  • When Hit – ta opcja pozwala z kolei na wykonanie dodatkowych akcji w momencie trafienia breakpointa, co obejmuje nawet możliwość uruchomienia makra sterującego zachowaniem IDE (!). Prawdopodobnie bardziej przydatne jest jednak wypisywanie komunikatu w oknie Output debugera; opcja ta jest na tyle zaawansowana, że może właściwie eliminować użycie funkcji typu OutputDebugString czy nawet dedykowanych logerów.
  • Condition to z kolei bodaj najprzydatniejsza opcja ze wszystkich. Pozwala mianowicie na określenie dodatkowego warunku, który musi być spełniony, aby punkt przerwania zadziałał. Jest to nieocenione do znajdowania błędów w skomplikowanych pętlach warunkowych, że o funkcjach rekurencyjnych nie wspomnę.
  • Filter to z kolei zaawansowana opcja, umożliwiająca ograniczenie działania breakpointa tylko do wybranych procesów lub wątków (rozpoznawanych przez identyfikator lub nazwę). Jak nietrudno się domyślić, przydaje się w programach równoległych.
  • W końcu Disable Breakpoint pozwala czasowo wyłączyć breakpoint bez usuwania go, czyli utraty tych wszystkich ustawień, które pieczołowicie wpisywaliśmy przy pomocy powyższych opcji :)

I tyle właśnie potrafią breakpointy w VS. Całkiem sporo, jak na jedną niepozorną, czerwoną kropkę :]

Tags: ,
Author: Xion, posted under Applications, Programming » 4 comments

C#: referencje i porównywanie

2008-11-17 14:54

W C# obiekty zwykle dostępne są poprzez referencje. Zatem zmienna typu T (jeśli jest on klasą) nie zawiera samego obiektu T, lecz tylko odwołanie do niego. Porównując dwie takie zmienne przy pomocy operatora == domyślnie sprawdzamy więc, czy pokazują one na ten sam obiekt. Podobnie jest też przy użyciu domyślnej wersji metody System.Object.Equals; robi ona dokładnie to samo, co wspomniany operator. Można mądrze powiedzieć, że oba mechanizmy sprawdzają relację identyczności obiektów.

Czasami jednak chodzi nam o coś innego: chcemy sprawdzić, czy dwa obiekty są równe, np. w sensie zawartości swoich pól. Taka równość może zachodzić także wtedy, gdy obiektyte zostały stworzone zupełnie niezależnie. Znów można mądrze stwierdzić, że chcąc dokonać takiego sprawdzenia, realizujemy dla obiektów semantykę wartości. Co wtedy zrobić?… Ano przeciążyć tudzież nadpisać wspomnianą metodę Equals i/lub operator ==.
I tu zaczynają się schody, bo wcale nie jest łatwo zrobić to poprawnie, a jeszcze trudniej jest zrobić to sensownie. Wszystko zależy od tego, czy nasza klasa ma realizować wyłącznie ową nieszczęsną semantykę wartości czy też czasami będziemy jednak sprawdzać, czy dwie referencje pokazują na ten sam obiekt (a nie na dwa równe obiekty).

  • Jeśli tak (tzn. tylko sprawdzanie równości obiektów), to powinniśmy przeciążyć zarówno operator ==, jak i metodę Equals – i to przeciążyć tak, aby działały tak samo. Wtedy zwykle zajmujemy się najpierw operatorem, a później wykorzystujemy go w metodzie:
    1. struct Foo
    2. {
    3.     private int field;
    4.  
    5.     public static bool operator == (Foo a, Foo b)    { a.field == b.field; }
    6.     public override bool Equals(Object obj)
    7.         { return obj is Foo && this == (Foo)obj; }
    8. }

    W takiej sytuacji prawdopodobnie powinniśmy też posłużyć się strukturą. Najpewniej chodzi nam bowiem o typ, który ma zachowywać się jak podstawowy: czyli coś w stylu wektora, macierzy, kwaternionu, itp.

  • W przeciwnym przypadku (czyli: czasem porównujemy wartości, a czasem referencje) klasa powinna pozostać klasą, lecz należy przedefiniować jej metodę Equals. To jej powinniśmy używać, do sprawdzenia relacji równości między obiektami. Kiedy zaś chcemy zwyczajnie porównać referencje, możemy wtedy uciec się do operatora ==.

Uff, całkiem to zawiłe, prawda? Niestety nie jest łatwo uchwycić różnicę między równością a identycznością – a w językach, które obiekty realizują przez referencję sytuacja komplikuje się jeszcze bardziej. Zaś już chyba całkiem rozmywa się wtedy, gdy niektóre typy traktujemy per wartość, a niektóre per referencja…
A tak przecież jest C#. I jednocześnie podobno to jeden z prostszych języków do nauki i użytkowania :D

Tags: , ,
Author: Xion, posted under Programming » 5 comments

Grzechy grafiki

2008-11-14 22:22

Karty graficzne mogą podołać renderowaniu coraz większej liczby wierzchołków, więc grafika w grach jest coraz bardziej szczegółowa – to oczywiste. Niezbyt oczywiste jest jednak to, czy jest ona faktycznie lepsza, bowiem nawet mając bardzo dobrze zrobione modele i tekstury niekoniecznie można złożyć je w przyzwoicie wyglądający świat.
Według mnie jest kilka głównych grzechów, jakie można przy tej okazji popełnić:


  • Model postaci ładny,
    ale co to za teren?

    Syndrom pustyni z drzewkami. Nazywam w ten sposób wrażenie, że na scenie obiekty niespecjalnie pasują do siebie. Przykładem czegoś takiego jest heightmapa pokryta niezbyt urozmaiconą teksturą (stąd ‘pustynia’), do której “przyklejone” są wyraźnie odcinające się drzewa. Taki teren jest jak najbardziej poprawny w demie silnika, ale w grze oczekiwalibyśmy trochę więcej urozmaicenia i trochę więcej spójności. Może jakieś trawy i krzaczki? ;)

  • Ocean w kałuży. Woda to okazja do wielu ciekawych efektów, z refleksami światła i odbiciami na czele. Jednak inaczej wygląda duży zbiornik wodny, a inaczej małe jeziorko. Dlaczego więc tak często widzimy oceany płaskie jak stół i stawy, w których wręcz szaleją sztormy? Czyżby tak trudno było dobrać wagi dla używanej w vertex shaderze funkcji sinus? :)

  • Uwaga na słońce! ;P

    Za dużo blooma. Odpowiednie efekty typu post-process to, oprócz upiększania sceny, także dobry sposób na to, by przyciągnąć uwagę do wybranych miejsc (w domyśle: tych lepiej zrobionych niż inne). Jeśli jednak przesadzi się z bloomem, lens-flare i innymi pseudorealistycznymi upiększaczami, to całość robi się mniej imponująca, a bardziej męcząca.

To na pewno nie wszystkie błędy, które dają się w miarę łatwo zauważyć. Wydaje mi się jednak, że te należą do jednych z łatwiejszych do popełnienia. W końcu skoro już napisaliśmy jakiś fajny shader, to warto go wcisnąć, gdzie się da, prawda? :) No cóż, nie zawsze jest to dobry kierunek postępowania…

Tags: , ,
Author: Xion, posted under Games, Thoughts » 5 comments

Trzy kroki do użycia biblioteki

2008-11-10 20:03

Żeby w C++ użyć w naszym programie kodu pochodzącego ‘z zewnątrz’ – a więc jakiejś biblioteki – trzeba się trochę napocić. Musimy bowiem odpowiednio “nakarmić” informacjami zarówno kompilator, jak i linker, co czasami skutkuje tym, że konieczne są w sumie trzy kroki. Są to:

  1. Dodanie odpowiedniej biblioteki statycznej (.lib) do wejścia linkera. W Visual C++ jest to uzupełnienie pola Additional Dependencies w zakładce Linker > Input we właściwościach projektu (można to także osiągnąć dyrektywami #pragma bezpośrednio w kodzie).
  2. Dołączenie odpowiednich plików nagłówkowych do naszego kodu – oczywiście przy pomocy dyrektywy #include. To ukłon w stronę kompilatora, aby mógł on poprawnie zidentyfikować wszystkie nazwy (funkcje, klasy, itp.) pochodzące z biblioteki.
  3. Odpowiednie potraktowanie ewentualnych przestrzeni nazw używanych przez kod zewnętrzny, czyli pisanie ich przed każdą nazwą (np. std::list zamiast list) lub skorzystanie z dyrektyw using.

To w sumie całkiem sporo pracy, wynikającej z niezbyt zautomatyzowanego sposobu budowania programów. W innych językach (głównie w tych, których pojęcie ‘projektu’ jest częścią ich samych, a nie tylko IDE) drugi z tych kroków często nie występuje w ogóle, bo został całkowicie zintegrowany z trzecim. Zawsze jednak trzeba wyraźnie wskazać, gdzie znajduje się kod biblioteczny, który ma zostać dołączony do naszego skompilowanego programu – czyli wykonać krok pierwszy. Ta czynność, często zapominana (bo zwykle nie znajdująca odzwierciedlenia w samym kodzie) jest bowiem najważniejsza.

Tags: ,
Author: Xion, posted under Programming » 1 comment

Kolejność na liście inicjalizacynej

2008-11-07 10:40

Jak wiadomo, do konstruktorów w C++ możemy doczepić listy inicjalizacyjne, pozwalające nadawać początkowe wartości polom macierzystej klasy (i nie tylko zresztą). Związany jest z tym pewien pozornie zaskakujący fakt, dotyczący kolejności inicjalizacji tych pól:

  1. class Foo
  2. {
  3.     private: string a, b;
  4.     public: Foo() : b("b"), a("a") { }
  5. };

Są one zawsze ustawiane w porządku zgodnym z ich deklaracjami w klasie. Tak więc powyżej inicjalizowane będzie najpierw pole a, zaś potem b – mimo że w konstruktorze kolejność jest odwrotna. Oczywiście w takim prostym przypadku nie ma to znaczenia, ale jeśli między polami występują zależności, wtedy może to być przyczyną “dziwnych” błędów. Porządniejsze kompilatory ostrzegają na szczęście przed rozbieżnymi kolejnościami pól w klasie i na liście w konstruktorze.

Można naturalnie zapytać, dlaczego w ogóle jest to zorganizowane tak nieintuicyjnie. Pierwsza nasuwająca się odpowiedź – “bo to C++” – nie jest bynajmniej zadowalająca :) Tak naprawdę przyczyną jest to, iż konstruktorów może być naturalnie więcej niż jeden:

  1. public:
  2.     Foo() : b("b"), a("a") { }
  3.     Foo(const Foo& foo) : a(foo.a), b(foo.b) { }

i mogą mieć one różną kolejność pozycji na liście inicjalizacyjnej. Gdyby to ona liczyła się bardziej, wtedy różne obiekty tej samej klasy byłyby konstruowane na różne sposóby. Co gorsza, sposób tej konstrukcji (kolejność inicjalizacji pól) musiałby zostać gdzieś zapamiętany na czas życia obiektu, jako że jego pola powinny być później zniszczone w kolejności odwrotnej do konstrukcji – zgodnie z zasadami języka.
Krótko mówiąc, zastosowanie “intuicyjnego” podejścia skutkowałoby nie tylko znacznie większymi niejasnościami, ale i dodatkowymi kłopotami zarówno dla programisty, jak i kompilatora.

Tags: , ,
Author: Xion, posted under Programming » Comments Off on Kolejność na liście inicjalizacynej
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.