Wyjątkowe piękno kodu źródłowego Doom 3

kod Los Doom 3 John Carmack Oda do kodu pc
2016-08-23 10:00.

To jest opowieść o kodzie źródłowym Doom 3 i jego piękna. Tak, piękna . Pozwólcie mi wyjaśnić.

Po wydaniu gry wideo Dyad zrobiłem sobie małą przerwę. Czytałem kilka książek i oglądałem filmy, które odkładałem zbyt długo. Pracowałem nad europejską wersją Dyad , ale wtedy głównie czekałem na opinie z działu kontroli jakości Sony, więc miałem dużo wolnego czasu. Po leniuchowaniu przez mniej więcej miesiąc zacząłem poważnie rozważać, co będę dalej robić. Chciałem wyodrębnić części Dyad wielokrotnego użytku / silnika do nowego projektu.

Ten artykuł ukazał się pierwotnie 14 stycznia 2013.

Kiedy zaczynałem pracę nad Dyad, istniał bardzo czysty, całkiem funkcjonalny silnik gry, który stworzyłem w wyniku wielu lat pracy nad innymi projektami. Pod koniec Dyady miałem okropny bałagan.

W ostatnich sześciu tygodniach rozwoju Dyad dodałem ponad 13 tys. Linii kodu. MainMenu.cc powiększył się do 24,501 linii. Niegdyś piękny kod źródłowy był bałaganem wypełnionym #ifdefs, darmowymi wskaźnikami do funkcji, brzydkim wbudowanym SIMD i kodem ASM - nauczyłem się nowego terminu: „entropia kodu”. Przeszukałem Internet w poszukiwaniu innych projektów, których mógłbym użyć, aby dowiedzieć się, jak organizować setki tysięcy linii kodu. Po przejrzeniu kilku dużych silników gier byłem dość zniechęcony; Diada kod źródłowy nie był rzeczywiście tak źle w porównaniu do wszystkiego innego tam!

Niezadowolony, kontynuowałem poszukiwania i znalazłem bardzo ładną analizę kodu źródłowego id Software do Doom 3 przez eksperta komputerowego Fabiena Sanglarda.

Spędziłem kilka dni przeglądając kod źródłowy Doom 3 i czytając doskonały artykuł Fabiena, kiedy napisałem na Twitterze:

To była prawda. Nigdy wcześniej tak naprawdę nie przejmowałem się kodem źródłowym. Nie uważam się za „programistę”. Jestem w tym dobry, ale dla mnie to tylko środek do celu. Przeglądanie kodu źródłowego Doom 3 sprawiło, że naprawdę doceniłem dobrych programistów.

Aby spojrzeć z perspektywy: Dyad ma 193 tys. Linii kodu, wszystko w C ++. Doom 3 ma 601 tys., Quake III ma 229 tys., A Quake II ma 136 tys. To stawia Dyad gdzieś pomiędzy Quake II i Quake III . To są duże projekty.

Kiedy poproszono mnie o napisanie tego artykułu, użyłem go jako wymówki, aby przeczytać więcej kodu źródłowego z innych gier i poczytać o standardach programowania. Po wielu dniach poszukiwań byłem zdezorientowany własnym tweetem, od którego wszystko się zaczęło: co właściwie oznaczałoby „ładnie wyglądający” - czy też „piękny” - w odniesieniu do kodu źródłowego? Zapytałem kilku znajomych programistów, co według nich oznacza. Ich odpowiedzi były oczywiste, ale nadal warte stwierdzenia:

Istnieje standard kodowania idTech 4 ( .doc ), który moim zdaniem wart jest przeczytania. Przestrzegam większości tych standardów i spróbuję wyjaśnić, dlaczego są dobre i dlaczego właśnie dzięki nim kod Doom 3 jest tak piękny.

Jedną z najmądrzejszych rzeczy, jakie widziałem w Doomie, jest ogólne użycie ich analizatora leksykalnego [ 1 ] i parsera [ 2 ] . Wszystkie pliki zasobów to pliki ascii o ujednoliconej składni, w tym: skrypty, pliki animacji, pliki konfiguracyjne itp .; wszystko jest takie samo. Dzięki temu wszystkie pliki mogą być odczytywane i przetwarzane przez jeden fragment kodu. Parser jest szczególnie niezawodny, obsługując główny podzbiór C ++. Trzymając się ujednoliconego parsera i leksera, wszystkie inne komponenty silnika nie muszą martwić się o serializację danych, ponieważ istnieje już kod do tego. To sprawia, że ​​wszystkie inne aspekty kodu są czystsze.

Kod Dooma jest dość sztywny, chociaż moim zdaniem nie wystarczająco sztywny w odniesieniu do const [ 3 ] . Const służy kilku celom, które moim zdaniem ignoruje zbyt wielu programistów. Moja zasada brzmi: „wszystko powinno być zawsze stałe, chyba że nie może być”. Chciałbym, żeby wszystkie zmienne w C ++ były domyślnie const. Doom prawie zawsze trzyma się zasady „brak wejścia-wyjścia”; co oznacza, że ​​wszystkie parametry funkcji są wejściami lub wyjściami, nigdy jednocześnie. Dzięki temu znacznie łatwiej jest zrozumieć, co się dzieje ze zmienną, gdy przekazujesz ją do funkcji. Na przykład:

Ta definicja funkcji sprawia, że ​​jestem szczęśliwy!

Z kilku konstatacji wiem wiele rzeczy:

Reguła const i brak parametrów wejściowych / wyjściowych jest prawdopodobnie najważniejszą rzeczą w moich oczach, która oddziela dobry kod od pięknego kodu. Dzięki temu cały system jest łatwiejszy do zrozumienia i łatwiejszy w edycji lub refaktoryzacji.

To kwestia stylistyczna, ale jedną piękną rzeczą, którą zwykle robi Doom , jest nie przesadzanie. Widziałem o wiele za dużo kodu, który wygląda tak:

Uważam to za wyjątkowo irytujące. Mogę powiedzieć, co robi ta metoda po jej nazwie. Jeśli jego funkcji nie można wywnioskować z nazwy, należy zmienić jej nazwę. Jeśli robi za dużo, by opisać to w nazwie, niech robi mniej. Jeśli naprawdę nie można go zmienić i zmienić nazwy, aby opisać jego jedyny cel, można to skomentować. Myślę, że w szkole uczy się programistów, że komentarze są dobre; nie są. Komentarze są złe, chyba że są całkowicie konieczne i rzadko są potrzebne. Doom wykonuje rozsądną robotę, ograniczając liczbę komentarzy do minimum. Korzystając z przykładu idSurface :: Split (), spójrzmy, jak jest komentowany:

// dzieli powierzchnię na przednią i tylną powierzchnię, sama powierzchnia pozostaje niezmieniona
// frontOnPlaneEdges i backOnPlaneEdges opcjonalnie przechowują indeksy krawędzi leżących na podzielonej płaszczyźnie
// zwraca SIDE_?

Pierwsza linijka jest zupełnie niepotrzebna. Wszystkie te informacje nauczyliśmy się z definicji funkcji. Druga i trzecia linia są cenne. Moglibyśmy wywnioskować właściwości drugiej linii, ale komentarz usuwa potencjalną niejednoznaczność.

Kod Dooma jest w większości sądowy z komentarzami, dzięki czemu jest znacznie łatwiejszy do odczytania. Wiem, że może to być kwestia stylu dla niektórych osób, ale zdecydowanie uważam, że istnieje jasny „właściwy” sposób, aby to zrobić. Na przykład, co by się stało, gdyby ktoś zmienił funkcję i na końcu usunął stałą? Następnie powierzchnia * MOŻE * zostać zmieniona z poziomu funkcji i teraz komentarz nie jest zsynchronizowany z kodem. Nieistotne komentarze szkodzą czytelności i dokładności kodu, czyniąc go brzydszym.

Doom nie marnuje miejsca w pionie:

Oto przykład z t_stencilShadow :: R_ChopWinding ():

Mogę odczytać cały algorytm na 1/4 mojego ekranu, pozostawiając pozostałym 3/4, aby zrozumieć, gdzie ten blok kodu pasuje do otaczającego go kodu. Widziałem za dużo takiego kodu:

To będzie kolejny punkt, który należy do „stylu”. Programowałem przez ponad 10 lat w tym drugim stylu, zmuszając się do przejścia na ściślejszą drogę podczas pracy nad projektem około sześć lat temu. Cieszę się, że się zmieniłem.

Ta ostatnia zajmuje 18 linii w porównaniu do 11 w pierwszej. To prawie dwukrotność liczby wierszy kodu dla tej samej funkcji * EXACT *. Oznacza to, że dla mnie następny fragment kodu nie mieści się na ekranie. Jaki jest następny kawałek?

Ten kod nie ma sensu bez poprzedniego fragmentu pętli for. Gdyby id nie respektował przestrzeni pionowej, ich kod byłby znacznie trudniejszy do odczytania, trudniejszy do napisania, trudniejszy w utrzymaniu i mniej piękny.

Inną rzeczą, którą robi id, a która moim zdaniem jest „właściwa” i nie jest kwestią stylu jest to, że * ZAWSZE * używają {}, nawet jeśli są opcjonalne. Myślę, że pomijanie nawiasów klamrowych jest przestępstwem. Widziałem tyle kodu, jak:

To jest brzydki kod, gorszy niż umieszczenie {} we własnym wierszu. Nie udało mi się znaleźć ani jednego przykładu w kodzie id, w którym pominęli {}. Pominięcie opcjonalnego {} sprawia, że ​​analizowanie tego bloku while () jest bardziej czasochłonne, niż powinno. To również sprawia, że ​​edycja jest uciążliwa, a co jeśli chciałbym wstawić gałąź instrukcji if w ścieżce else if (c> d)?

id zrobił wielkie nie-nie w świecie C ++. Ponownie napisali wszystkie wymagane funkcje STL [ 4 ] . Osobiście mam miłość-nienawiść do STL. W Dyad użyłem go w kompilacjach debugowania do zarządzania zasobami dynamicznymi. W wersji wypaliłem wszystkie zasoby, aby można je było załadować tak szybko, jak to możliwe i nie używać żadnych funkcji STL. STL jest fajny, ponieważ zapewnia szybkie ogólne struktury danych; jest zły, ponieważ używanie go często może być brzydkie i podatne na błędy. Na przykład spójrzmy na klasę std :: vector <T>. Powiedzmy, że chcę iterować po każdym elemencie:

Upraszcza to w C ++ 11:

Osobiście nie podoba mi się używanie auto, myślę, że dzięki temu kod jest łatwiejszy do napisania, ale trudniejszy do odczytania. Być może w najbliższych latach dojdę do używania samochodu, ale na razie myślę, że jest źle. Nie zamierzam nawet wspominać o absurdalności niektórych algorytmów STL, takich jak std: for_each czy std :: remove_if.

Usunięcie wartości ze std :: vector też jest głupie:

Ojej, to będzie za każdym razem poprawnie wpisywane przez każdego programistę!

id usuwa wszelkie niejednoznaczności: zwinęli swoje własne generyczne kontenery, klasę stringów itp. Napisali je o wiele mniej ogólnikowo niż klasy STL, prawdopodobnie po to, aby były łatwiejsze do zrozumienia. Mają minimalny szablon i używają alokatorów pamięci specyficznych dla identyfikatora. Kod STL jest tak zaśmiecony szablonowymi bzdurami, że nie można go odczytać.

Kod C ++ może szybko stać się niesforny i brzydki bez staranności ze strony programistów. Aby zobaczyć, jak złe rzeczy mogą się wydarzyć, sprawdź kod źródłowy STL. Implementacje STL Microsoftu i GCC [ 5 ] są prawdopodobnie najbrzydszym kodem źródłowym, jaki kiedykolwiek widziałem. Nawet jeśli programiści bardzo się starają, aby ich kod szablonu był jak najbardziej czytelny, nadal jest to kompletny bałagan. Spójrz na bibliotekę Loki Andrei Alexandrescu lub biblioteki boost - są one napisane przez jednych z najlepszych programistów C ++ na świecie i dołożono wszelkich starań, aby były tak piękne, jak to tylko możliwe, i nadal są brzydkie i w zasadzie nieczytelne.

id rozwiązuje ten problem, po prostu nie czyniąc rzeczy zbyt ogólnikowymi. Mają klasę HashTable <V> i HashIndex. HashTable wymusza na typie klucza const char *, a HashIndex to para int-> int. Jest to uważane za kiepską praktykę C ++. Powinni byli mieć jedną klasę HashTable i napisać częściową specjalizację dla KeyType = const char * oraz w pełni wyspecjalizowaną <int, int>. To, co robi id, jest całkowicie poprawne i sprawia, że ​​ich kod jest znacznie piękniejszy.

Można to dokładniej zbadać porównując „dobrą praktykę C ++” do generowania skrótu i ​​jak robi to id.

Wiele osób uważa, że ​​dobrą praktyką byłoby utworzenie określonej klasy obliczeniowej jako parametru tabeli HashTable, w następujący sposób:

mogłoby to wtedy być wyspecjalizowane dla określonego typu:

Następnie możesz przekazać ComputeHashForType jako HashComputer dla HashTable:

Jest to podobne do tego, jak to zrobiłem. Wydaje się mądry, ale chłopcze, czy to brzydkie! Co by było, gdyby było więcej opcjonalnych parametrów szablonu? Może alokator pamięci? Może znacznik debugowania? Miałbyś definicję taką jak:

Definicje funkcji byłyby brutalne!

Co to w ogóle znaczy? Nie mogę nawet znaleźć nazwy metody bez agresywnego podświetlania składni. Można sobie wyobrazić, że będzie więcej kodu definicji niż kodu ciała. To wyraźnie nie jest łatwe do odczytania, a zatem nie jest piękne.

Widziałem, jak inne silniki radzą sobie z tym bałaganem, przenosząc specyfikację argumentów szablonu na niezliczone typy definicji. To jest jeszcze gorsze! Może to uczynić kod lokalny łatwiejszym do zrozumienia, ale tworzy kolejną warstwę rozłączenia między kodem lokalnym a nadrzędną logiką systemu, sprawiając, że kod lokalny nie wskazuje na projekt systemu, co nie jest piękne. Na przykład, powiedzmy, że był kod:

i

i użyłeś obu i zrobiłeś coś takiego:

Jest możliwe, że alokator pamięci StringHashTable, StringAllocator, nie będzie miał udziału w pamięci globalnej, co spowoduje zamieszanie. Musiałbyś cofnąć się przez kod, dowiedzieć się, że StringHashTable jest w rzeczywistości typem bałaganu szablonów, przeanalizować kod szablonu, dowiedzieć się, że używa innego alokatora, znaleźć ten alokator ... bla, brzydki.

Doom robi całkowicie „złe” rzeczy zgodnie z powszechną logiką C ++: pisze rzeczy tak nieogólne, jak to tylko możliwe, używając typów generycznych tylko wtedy, gdy ma to sens. Co robi Doom 's HashTable, gdy musi wygenerować skrót? Wywołuje idStr :: GetHash (), ponieważ jedynym typem klucza, jaki akceptuje, jest const char *. Co by się stało, gdyby potrzebował innego klucza? Domyślam się, że utworzą szablon klucza i wymuszą wywołanie key.getHash (), a kompilator wymusi, że wszystkie typy kluczy mają metodę int getHash ().

Nie wiem, ile oryginalnego zespołu programistów id jest w firmie, ale John Carmack przynajmniej pochodzi z C. Wszystkie gry id przed Quake III były napisane w C. Uważam, że wielu programistów C ++ bez silnego tła w C w porównaniu z C ++ zmienia swój kod. Poprzedni przykład szablonu był tylko jednym przypadkiem. Trzy inne przykłady, które często znajduję, to:

id jest bardzo sądowy we wszystkich tych przypadkach.

Często można stworzyć klasę:

To strata linii kodu i czasu na czytanie. Zapisywanie i czytanie zajmuje więcej czasu w porównaniu z:

A co, jeśli często zwiększasz var o jakąś liczbę n?

vs

Pierwszy przykład jest znacznie łatwiejszy do czytania i pisania.

id nie używa stringstreams. Strumień łańcuchowy zawiera prawdopodobnie najbardziej ekstremalną bastardyzację przeciążeń operatorów, jakie kiedykolwiek widziałem: <<.

Na przykład:

To jest brzydkie. Ma to mocne zalety: można zdefiniować odpowiednik metody toString () Javy dla każdej klasy bez dotykania vtables klasy, ale składnia jest obraźliwa i id zdecydował się nie używać. Wybór printf () zamiast stringstreams sprawia, że ​​ich kod jest łatwiejszy do odczytania i dlatego uważam, że jest to właściwa decyzja.

Dużo ładniejszy!

Składnia operatora SomeClass << też byłaby śmieszna:

[ Nota boczna: John Carmack stwierdził, że narzędzia do analizy statycznej ujawniły, że ich częstym błędem było nieprawidłowe dopasowanie parametrów w printf (). Zastanawiam się, czy z tego powodu zmienili się na stringstreams w Rage . GCC i clang zarówno findf () parametry dopasowania błędów z -Wall, więc nie potrzebujesz drogich narzędzi do analizy statycznej, aby znaleźć te błędy.]

Kolejną rzeczą, która sprawia, że kod Doom jest piękny, jest minimalne użycie przeciążeń operatorów. Przeciążanie operatorów to bardzo fajna funkcja C ++. Pozwala na:

Bez przeciążania tych operacji pisanie i analizowanie byłoby bardziej czasochłonne. Doom kończy się tutaj. Widziałem kod, który tego nie robi. Widziałem kod, który przeładowuje operator '%', aby oznaczać iloczyn skalarny lub operator Vector * Vector, aby wykonać mnożenie wektorów fragmentarycznie. Nie ma sensu tworzyć operatora * dla cross-product, ponieważ istnieje on tylko w 3D, a co by było, gdybyś chciał:
some_2d_vec * some_2d_vec, co powinien zrobić? A co z 4d lub wyższym? Minimalne przeciążenie operatora id nie pozostawia niejednoznaczności dla czytelnika kodu.

Jedną z największych rzeczy, których nauczyłem się z kodu Doom, była prosta zmiana stylu. Kiedyś miałem zajęcia, które wyglądały następująco:

Zgodnie ze standardem kodowania Doom 3 id , używają prawdziwych tabulatorów, które mają 4 spacje. Posiadanie spójnych ustawień zakładek dla wszystkich programistów umożliwia im poziome wyrównanie definicji klas:

Rzadko umieszczają funkcje wbudowane wewnątrz definicji klasy. Widziałem go tylko raz, gdy kod jest zapisany w tym samym wierszu, co deklaracja funkcji. Wydaje się, że ta praktyka nie jest normą i prawdopodobnie jest źle widziana. Ta metoda organizowania definicji klas sprawia, że ​​jest ona niezwykle łatwa do przeanalizowania. Pisanie może zająć trochę więcej czasu, ponieważ podczas definiowania metod musiałbyś ponownie wpisać kilka informacji:

Jestem przeciwny wszelkiemu dodatkowemu wpisywaniu. Muszę robić rzeczy tak szybko, jak to możliwe, ale jest to jedna sytuacja, w której myślę, że trochę dodatkowego wpisywania podczas definiowania klasy więcej niż się opłaca za każdym razem, gdy definicja klasy musi zostać przeanalizowana przez programistę. W Doom 3 Coding Standards ( .doc ) jest kilka innych stylistycznych przykładów, które przyczyniają się do piękna kodu źródłowego Dooma .

Myślę Doom zasady metoda „s nazewnictwa brakuje. Osobiście egzekwuję zasadę, że wszystkie nazwy metod powinny zaczynać się od czasownika, chyba że nie mogą.

Na przykład:

jest znacznie lepszy niż:

Byłem bardzo podekscytowany, mogąc napisać ten artykuł, ponieważ dał mi on pretekst do przemyślenia tego, czym jest piękny kod. Nadal nie sądzę, żebym wiedział, i może to całkowicie subiektywne. Wydaje mi się, że dwie najważniejsze rzeczy, przynajmniej dla mnie, to stylistyczne wcięcie i maksymalna konsekwencja.

Wiele stylistycznych wyborów jest zdecydowanie moimi osobistymi preferencjami i jestem pewien, że inni programiści będą mieli inne zdanie. Myślę, że wybór stylu zależy od tego, kto musi przeczytać i napisać kod, ale z pewnością uważam, że jest to coś, o czym warto pomyśleć.

Sugerowałbym, aby wszyscy spojrzeli na kod źródłowy Doom 3, ponieważ uważam, że stanowi on przykład pięknego kodu, jako kompletny pakiet: od projektu systemu po sposób umieszczania znaków w tabulacji.

Shawn McGrath to deweloper gier z Toronto i twórca uznanej psychodelicznej gry wyścigowej Dyad na PlayStation 3. Dowiedz się więcej o jego grze . Śledź go na Twitterze .

Przypisy

[ 1 ] Analizator leksykalny konwertuje znaki kodu źródłowego (w odpowiednim kontekście) na serię tokenów o znaczeniu semantycznym. Kod źródłowy może wyglądać następująco:

x = y + 5;

Analizator leksykalny (lub w skrócie „lekser”) może tokenizować to źródło jako takie:
x => zmienna
= => operator przypisania
y => zmienna
+ => dodatkowy operator
5 => literalna liczba całkowita
; => koniec instrukcji

Ten ciąg tokenów jest pierwszym z wielu kroków w konwersji kodu źródłowego do działającego programu. po analizie leksykalnej tokeny są wprowadzane do parsera, następnie kompilatora, następnie konsolidatora, a na końcu maszyny wirtualnej (w przypadku języków kompilowanych procesor). Między tymi głównymi krokami mogą znajdować się kroki pośrednie, ale te wymienione na liście są ogólnie uważane za najbardziej podstawowe.

[ 2 ] Parser jest (zwykle) następnym logicznym krokiem po analizie leksykalnej w maszynowym rozumieniu języka (język komputerowy / kod źródłowy w tym kontekście, ale to samo dotyczy języka naturalnego). Dane wejściowe parsera to lista tokenów generowanych przez analizator leksykalny, które generują drzewo składniowe: „drzewo analizy”.

W przykładzie: x = y + 5, drzewo analizy wyglądałoby następująco:

[ 3 ] „const” jest słowem kluczowym C ++, które zapewnia, że ​​zmienna nie może zostać zmieniona lub że metoda nie zmieni zawartości swojej klasy. „Stała” to skrót od „stała”. Warto zauważyć, że C ++ zawiera obejście, albo przez const_cast [T], albo rzutowanie w stylu C: (T *). Korzystanie z nich całkowicie łamie const i ze względu na argumentację wolę zignorować ich istnienie i nigdy nie używać ich w praktyce.

[ 4 ] STL oznacza „standardową bibliotekę szablonów”. Jest to zestaw kontenerów, algorytmów i funkcji powszechnie używanych przez programistów C ++. Jest obsługiwany przez wszystkich głównych dostawców kompilatorów z różnymi poziomami optymalizacji i możliwościami raportowania błędów.

[ 5 ] GCC - Kolekcja kompilatorów GNU: zestaw kompilatorów obsługujących wiele języków programowania. W przypadku tego artykułu odnosi się to do kompilatora GNU C / C ++. GCC jest darmowym kompilatorem z pełnym kodem źródłowym dostępnym za darmo i działa na szerokiej gamie komputerów i systemów operacyjnych. Inne powszechnie używane kompilatory to: clang, Microsoft Visual C ++, IBM XL C / C ++, Intel C ++ Compiler.

Suggested posts

Pięć nocy u twórcy Freddy'ego, Scott Cawthon, przechodzi na emeryturę pośród kontrowersji

Pięć nocy u twórcy Freddy'ego, Scott Cawthon, przechodzi na emeryturę pośród kontrowersji

Scott Cawthon, niezależny twórca najbardziej znany jako twórca serii Five Nights at Freddy, ogłosił odejście od profesjonalnego tworzenia gier. „Miałem błogosławioną, satysfakcjonującą i bogatą karierę”, napisał Cawthon na swojej osobistej stronie internetowej.

Nawet jeśli chodzi o anime, Netflix wciąż mówi, że pozostaje w tyle w Japonii

Nawet jeśli chodzi o anime, Netflix wciąż mówi, że pozostaje w tyle w Japonii

Netflix ma doskonały wybór programów anime. Spójrz na dziesięć najlepszych list przebojów na Netflix Japan, które są wypełnione anime.

Related posts

Raport: Sony wypuszcza nowe gogle VR w 2022 r. .

Raport: Sony wypuszcza nowe gogle VR w 2022 r. .

Oryginalna PlayStation VR, pierwotnie wydana w 2016 roku Raport Bloomberga, który dotyczy głównie paneli LCD i VR, zawiera również krótką wzmiankę, że Sony planuje wydać nowy zestaw słuchawkowy VR pod koniec 2022 roku. Podczas gdy większość historii dotyczy firmy Japan Display Inc. odejście od dostarczania ekranów LCD Apple i zwiększenie liczby zestawów VR, zauważa również, że Sony nie jest częścią tego trendu i planuje „korzystać z Samsung Display Co.

Japońska seria Giant Robot Crossover powraca w tym, co wydaje się jej 1174. grą

Japońska seria Giant Robot Crossover powraca w tym, co wydaje się jej 1174. grą

Super Robot Wars, jedna z najdłużej działających i najliczniejszych serii gier wideo wszechczasów, powraca ponownie, tym razem świętując swoje 30. urodziny.Seria taktyczna, w której rzuca się wiele najbardziej lubianych japońskich gigantycznych postaci robotów/mechów do wspólnej walki, na przestrzeni lat zawierał postacie z Gundam, Mazinger, Macross i Neon Genesis (żeby wymienić tylko kilka) w jednym miejscu.

Adam z Metroid Dread nie zamówi Samusa, mówi Nintendo

Adam z Metroid Dread nie zamówi Samusa, mówi Nintendo

To był pracowity dzień na E3 2021, a największym ogłoszeniem jest prawdopodobnie wznowienie przez Nintendo od dawna plotkowanego tytułu Metroid Dread w piątej głównej części serii. Metroid Dread, który pojawi się na Switchu 8 października, ponownie śledzi Samus Aran podczas jej planetarnych eksploracji, ale pojawia się również Adam Malkovich, kontrowersyjna postać wśród fanów Metroid.

Gry na konsolę Xbox otrzymują nową grafikę pudełek

Gry na konsolę Xbox otrzymują nową grafikę pudełek

Niecały rok po premierze Xbox Series X|S firma Microsoft zmienia standardową grafikę pudełek dla gier na konsolę, aby ułatwić klientom stwierdzenie, z którą konsolą Xbox gra Xbox na półce jest zgodna .Xbox News po raz pierwszy zauważył tę zmianę: Powiększmy i przyjrzyjmy się bliżej: zamieniają więc mały czarny pasek na większe białe pole i usuwają stary zielony pasek z górnej części pudełka i zastępują go małe logo Xbox po lewej stronie.

MORE COOL STUFF

Dlaczego Al Pacino przepisał kulminacyjną scenę w sali sądowej w „I sprawiedliwość dla wszystkich”

Dlaczego Al Pacino przepisał kulminacyjną scenę w sali sądowej w „I sprawiedliwość dla wszystkich”

Al Pacino zaskoczył Normana Jewisona, mówiąc, że przepisał punkt kulminacyjny „I sprawiedliwość dla wszystkich”. Ale Pacino miał swoje powody.

Obsada „RHOC” Shakeup: Kelly Dodd Out, Heather Dubrow powraca w sezonie 16

Obsada „RHOC” Shakeup: Kelly Dodd Out, Heather Dubrow powraca w sezonie 16

Kelly Dodd została usunięta z 16 sezonu „RHOC” i nie wróci, ponieważ Heather Dubrow wraca po swoją pomarańczę.

Kevin Hart wyłącza dźwięk w Cancel Culture — „Ostatnim, co sprawdzałem, jedynym sposobem, w jaki dorastasz, jest pieprzenie się”

Kevin Hart wyłącza dźwięk w Cancel Culture — „Ostatnim, co sprawdzałem, jedynym sposobem, w jaki dorastasz, jest pieprzenie się”

Kevin Hart niedawno wypowiedział się przeciwko kulturze anulowania, zauważając, że osoby publiczne są jedynymi, od których oczekuje się, że będą doskonałe.

Kiedy pisarze „Sopranos” zwrócili się do Hitchcocka „North by Northwest” w poszukiwaniu inspiracji

Kiedy pisarze „Sopranos” zwrócili się do Hitchcocka „North by Northwest” w poszukiwaniu inspiracji

Pod koniec „Soprano” pisarze poszli na efekt Alfreda Hitchcocka, tworząc alternatywną tożsamość dla Tony'ego Soprano.

Chcesz idealnego Cuppa Joe? Piecz własne ziarna kawy

Chcesz idealnego Cuppa Joe? Piecz własne ziarna kawy

Szukałeś na świecie idealnej filiżanki kawy? Być może najlepszym rozwiązaniem jest nauka sztuki i nauki palenia własnych ziaren kawy.

Kim był tajemniczy Melchizedek w Biblii?

Kim był tajemniczy Melchizedek w Biblii?

W Księdze Rodzaju pojawia się tylko raz, a jednak był postrzegany jako prekursor Jezusa Chrystusa. Kim naprawdę był i jak związał się z Jezusem?

Fannie Lou Hamer: Od Sharecroppera do ikony praw obywatelskich i wyborczych

Fannie Lou Hamer: Od Sharecroppera do ikony praw obywatelskich i wyborczych

Urodzona w rodzinie ubogich dzierżawców w Mississippi, Fannie Lou Hamer została sekretarzem terenowym Studenckiego Komitetu Koordynacyjnego ds. Niestosowania Przemocy (SNCC) i niestrudzoną bojowniczką o prawa obywatelskie i prawa wyborcze.

Idealna burza ogólnoświatowych katastrof powoduje globalny niedobór półprzewodników

Idealna burza ogólnoświatowych katastrof powoduje globalny niedobór półprzewodników

Współczesna cywilizacja jest coraz bardziej zależna od półprzewodników, ale łańcuch dostaw został zakłócony przez pandemię COVID-19, susze i inne problemy, tak jak rośnie popyt.

Texas Man aresztowany po rzekomym przeciągnięciu byłego mamy za ciężarówkę i podpaleniu pojazdu

Texas Man aresztowany po rzekomym przeciągnięciu byłego mamy za ciężarówkę i podpaleniu pojazdu

Robert Eugene Hoffpauir, lat 37, został aresztowany i oskarżony o zabójstwo 60-letniego Romana Rodrigueza, według biura szeryfa hrabstwa Liberty

Leona Lewis mówi, że została „głęboko zraniona” Michaela Costello po tym, jak oskarżył Chrissy Teigen o zastraszanie

Leona Lewis mówi, że została „głęboko zraniona” Michaela Costello po tym, jak oskarżył Chrissy Teigen o zastraszanie

„Kiedy ludzie przepraszają (Chrissy) i okazują szczere wyrzuty sumienia i rehabilitację za swoje czyny, powinniśmy ich przyjąć i nie próbować kopać ich, gdy są przygnębieni” – napisała Leona Lewis na Instagramie

Kelsey Grammer płacze, gdy wspomina spotkanie z Paris Jackson, gdy była dzieckiem z tatą Michaelem Jacksonem

Kelsey Grammer płacze, gdy wspomina spotkanie z Paris Jackson, gdy była dzieckiem z tatą Michaelem Jacksonem

Kelsey Grammer po raz pierwszy spotkał swoją gwiazdę The Space Between, Paris Jackson, kiedy była młodsza i była świadkiem uroczej chwili między nią a tatą Michaelem Jacksonem

Reżyser Jackass 4, Jeff Tremaine, przyznał nakaz powstrzymywania się na trzy lata przeciwko Bam Margera

Reżyser Jackass 4, Jeff Tremaine, przyznał nakaz powstrzymywania się na trzy lata przeciwko Bam Margera

Jeff Tremaine, lat 54, złożył wniosek o zakaz zbliżania się do Bam Margera, lat 41, po tym, jak była gwiazda telewizji rzekomo wysłała mu i jego rodzinie groźby śmierci

Czy jesteś sędzią?

Nauka mówi, że wszyscy jesteśmy, i to niekoniecznie jest złe.

Czy jesteś sędzią?

Wiesz, kiedy kogoś spotykasz i nie możesz pomóc, ale robisz notatki w myślach. Albo ktoś przyłapie cię, że robisz im śmierdzące oko, gdy robią coś wątpliwego.

Krzysztof Kolumb nie może odróżnić manata od syreny

Krzysztof Kolumb nie może odróżnić manata od syreny

Podczas żeglugi po wodach wokół Haiti 9 stycznia 1493 roku słynny odkrywca Krzysztof Kolumb zauważył coś, co uważał za trzy syreny bawiące się w wodzie. Opowiadał później, że „wyszły dość wysoko z wody”, ale „nie były tak ładne, jak są przedstawiane, bo jakoś z wyglądu wyglądają jak mężczyźni.

Widok pola po 50 klientach

Wiele do zrobienia, ale można to zrobić

Widok pola po 50 klientach

Kiedy w 2019 roku ogłoszono, że zdecydowałem się odejść od California Symphony, aby wywrzeć szerszy wpływ poza jedną organizację, zanim pokieruję inną instytucją muzyki klasycznej, wrota otworzyły się w najlepszy sposób. Kilka miesięcy później cała nasza praca zmieniła się bardziej, niż kiedykolwiek sądziliśmy, że jest to możliwe, ponieważ koronawirus nagle zatrzymał działalność, jaką znaliśmy, otwierając nowe pytania i wyzwania dla naszych organizacji i pola.

Jakie tajemnice kryją się za powierzchnią starych obrazów?

Nowoczesna technologia i staromodna pomysłowość ujawniają zaskakujące odkrycia

Jakie tajemnice kryją się za powierzchnią starych obrazów?

Zrobił to Vincent Van Gogh i Pablo Piccaso też. Artyści malowali płótna z wielu powodów.

Language