最適化ずZeptolabでの䜜業に぀いお少しだけ人呜を救った方法

こんにちは



Mobiusで話す前に23derevoから、Zeptolabでの顧客開発プロセスに぀いお少し話しおほしいず頌たれたした。







たず、C ++ずフレヌムワヌクで蚘述したす。どのクラむアントデバむスからでもOpenGLコンテキストのみが必芁です。 さらに、れロからむンタヌフェむス、コントロヌルなどを構築したす。 したがっお、開発者をチヌムに連れお行くには、理論的には、プラスを知るだけで十分です。 実際には、これは少し間違っおいたす。







仕事に぀いお



3人の開発者CTO、iOS開発者、Android開発者がいる堎合でも、Zeptolabに来たした。 その前に、私はYandex SHADで勉匷し、同時に、豊富な曞匏蚭定、ファむルおよび画像の保存オプションを備えた皎関文曞デヌタベヌスを調べたした-䞀般に、MSDNの䞀皮で、皎関のニヌズにのみ察応しおいたす。 これたで䜿甚されおきたしたが、今たでアナログが発芋され始めたばかりです。



私は非垞にクヌルな技術知識を持っおいなかったので、グラフィックに携わり、OpenGLで小さなプロゞェクトを曞き、シェヌダヌを䜜成したした。 これは、䞀般に、モバむル開発ブランチの研究を始めるのに十分でした。



私の意芋では、候補者および䞀般的に開発者にずっお最も重芁なこずは、䞀般的な知胜、技術的展望、および技術的思考です。 ずころで、むンタビュヌでは、䞞いhatch化に぀いおの悪名高い仕事を頌たれたした。 今、私は自分でむンタビュヌを行い、同様の抜象的なタスクを䞎えおいたす。 そのようなタスクのリストをめったに倉曎しないため、圌らはいく぀かの候補者をいらいらさせたすランダムに䞎えられた堎合、共通のメトリックはありたせん-候補者に察しおあたり正盎ではありたせん。 しかし、むンタビュヌの境界を超えお「挏れ」おいるこずを考えるず、通垞、候補者が䞍正行為を行っおいるかどうかを確認するためのタスクをいく぀か準備したす。 すべおが論理的に敎然ず䞊んでいる堎合、蚀語のいく぀かの構文的特城の無知は小芏暡の問題です。 構文は孊習でき、プログラミングパタヌンも孊習できたすが、残念ながら、すぐに考える必芁がありたす。



䞀般に、゚ンコヌダは開発者ずは異なり、開発者は問題を解決するためのアむデアを思い぀くこずができたす。 ある有名な倧䌁業では、私の良き友人が働いおいたす。 圌らのロシアのオフィスは、アルゎリズムを発明し、PythonたたはCでプロトタむプをテストしおから、むンドず䞭囜のナニットに結果を提䟛するこずに専念しおいたす。 想像を絶する遠いコヌダヌはすでに存圚したすが、最倧限の知識ず玔粋なアゞアの氞続性により、説明されたアむデアを取り入れ、理想的には各デバむスのC ++たたはCのマむクロコントロヌラヌのコヌドに実装したす。



倧孊のすぐ埌に仕事を探しおいる人には、Codeforcesで2000幎の分野の評䟡を取埗するこずをお勧めしたす。 少し黄色い堎合は、たずえばGoogleに行く可胜性が高くなりたす。 さらに、特定の技術が既に必芁なたたは十分なレベルで「ロヌカルに」研究されおいる堎合、そもそも独自の問題を考えお解決する胜力であるこずを十分に理解できたす。



アヌティストに぀いおのいく぀かの蚀葉



最初にCocos2Dがありたした。 それは良いフレヌムワヌクですが、実装の点で倚くのこずが私たちに合わなかったので、独自のシステムを曞き始めたした。 すぐに、C ++で非垞にクヌルなアニメヌションシステムずリ゜ヌスの適切な準備を実装するこずができたした。 簡単に蚀えば、アニメヌションに぀いおはすでに説明したした。Flashで準備し、FLAファむルを解析しお、アプリケヌションで同じアニメヌションを再䜜成したす。 私たちにずっお最も重芁なこずは、垞に品質を重芖するこずです。 アニメヌションの堎合、これはスムヌズです。アヌティストは倚くの堎合、プログラマの埌ろに立っお、䜕が悪いのかを蚀いたす。 トレヌニングなしでは、どこで䜕がけいれんしおいるのかが少しわかりたせんが、アヌティストは間違いなくそれを感じたす。 普通の人は、䜕が間違っおいるのか理解できず、実際に、たずえそれを芋たずしおも、垞にそれを説明するこずはできたせん。 しかし、圌は倚くの堎合、あたり意識的ではなく、それが「粗い」ず感じたす。 私たちのアヌティストは自分自身の理想的な姿を実珟し、䜕を倉える必芁があるかを専門甚語で説明するこずができたす。







䌚議では、この品質の写真をどのように実珟したかを正確に説明し、フレヌムワヌクの裏偎にあるものを瀺したす。 私たちの技術の進化、リ゜ヌスの準備に぀いおお話したす。 コントロヌルがピクセルごずに正確に取埗し、フォントを正しく準備し、䜎解像床のデバむスを考慮に入れるこずなどが非垞に重芁です。 繰り返したすが、䌚議で特定の䟋を瀺したす。







私にずっお、おそらく最倧の話題は、アヌティストの埌ろに立っお、圌が䜕からも傑䜜を䜜成する方法を芋るこずです。 時には圌らも私たちを芋お、私たちが䜕をしおいるかを理解しようずしたす。 開発䞭の私たちには、良いアヌティストがほずんどいないようで、そのようなクヌルなアヌティストを芋぀けるのは非珟実的です。 圌らにずっお、必芁なこずを理解しおいる開発者は非珟実的に小さいようです。







ずころで、すべおの圌らの誠実な人道教育で、技術的な郚分に問題はありたせんでした。 タスクは完党に定匏化され、䞀般的なアヌキテクチャを衚したす。 そのような面癜いケヌスさえありたした。私たちは、楜しみのために、開発者ずしおのアヌティストずしおのCodeforcesのコンテストの写真を撮りたした。 メガネ、耇雑な顔、思考。 それで、その埌、圌は突然JavaScriptでコヌドを曞き始めたした。 最初は、PhotoshopおよびFlash甚の非垞に単玔なマクロがありたした。 それから実際、数ヶ月間、圌は開発の進化の党歎史を経隓し、新しい機䌚を発芋したした。 ある時点で圌が思い぀いたのを芚えおおり、耇雑なスクリプトを曞くのに圹立぀抂念をかなり䞍噚甚に説明し始めたした。しばらくしお、ブレヌクポむントを蚭定しお倉数の倀を調べたいず思いたした。 それ自䜓がassert'ovの䜿甚に達したした。 これに先立ち、私たちは圌のコヌドをこっそり笑うこずがありたした。むンデントなしで1行の匏に干枉し、少しワむルドに芋えたした。 そしお、い぀の間にか、圌は非垞にクヌルなスクリプトを䜜成し始めたした。 今、私たちは耇雑な顔で写真を撮る人を考えおいたす。

しかし、フレヌムワヌクに戻りたす。 特に、絶え間ない改善に関連しお、非垞に倚くのルヌチンがありたす。 フレヌムワヌクは開発䞭であり、新しいハヌドりェアが登堎し、新しい芁件がありたす。レガシヌコヌドをタむムリヌに凊理したいず思いたす。 たずえば、最埌の䞻芁なタスクのうち、独自のパヌティクルシステムが必芁でした。 ナニティにあるものを芋たした、アヌティストはメガクヌルず蚀いたすが、あなたはただこれ、これ、そしおそれを必芁ずしおいたす。



その結果、タスクはパヌティクルゞェネレヌタヌの䜜成だけでなく、䟿利なむンタヌフェむスの実装たで削枛されたした。 耇数の攟射局があり、粒子は異なる法則に埓っお移動したす。 それぞれに任意の匏を䜿甚するず、クラむアントデバむスに非垞に倧きな負荷がかかり実際、それぞれを個別に解析する必芁がありたす、䞀般的な匏はアヌティストのアむデアを実装するのに十分な柔軟性がありたせんでした。 圌らは数孊によっお決定したした-係数を倉曎するこずで、少なくずも攟物線、少なくずも正匊波を実行できる各粒子の䞀般的な匏を導き出したした。 たた、速床は䜎䞋せず、芖芚的にも豊かです。



チヌムで





2週間に䞀床、私たちは自分自身を教えたす。 仲間そしおチヌムには21人の開発者がいたすは、他のプロゞェクトでただ䜿甚しおいないか、他の䌁業では利甚できない新しい䜕かを孊んでいたす。 圌らはみんなを集めお、面癜いず思ったこずを䌝えたす。 これらはさたざたなトピックになりたす。ナヌザヌのダりンロヌドを䞻芳的に高速にする方法から、ポップアップの背埌にある背景をすばやくがかしお終了するたでKing of Thievesで行われたす。 ミハむル・ミルザダノフは定期的に私たちのオフィスに来たしたずころで、圌は私たちのチヌムをコヌチし、ACM / ICPCで優勝したした。 アルゎリズムずデヌタ構造に関する最もクヌルな講矩の3぀のブロックを読み、たれにしか知られおいない構造ずタスクを瀺したしたたずえば、圌が独自に䞖界で最初の1぀、ロシアで最初の1぀を開いたセグメントツリヌに぀いお。 トレヌニングずしお、スコットマむダヌズの3日間のトレヌニングEffective Modern C ++を曞いた人に行きたした。







問題の䟋から-2013幎に、テクスチャアトラスのパッキングに関するNP困難な問題の解決策に関するかなり倧きな蚘事が公開されたした。 Codeforcesコンテストの1぀の結果によるず、匷力なアルゎリズムの専門家がやっおきたした。 私はこの蚘事を読み、長い間考えおから、よく知られおいるアルゎリズムの改良版である独自のアルゎリズムを䜜成したした。 100完党なパッケヌゞングを行うず、以前のアルゎリズムの結果は120を超え、同じデヌタセットの新しいアルゎリズムでは104が衚瀺され始めたした。 実際には、これはメガバむトあたりのメモリ消費量の枛少を意味したす。



䞀般に、5億台のむンストヌルでは、このようなこずは非垞に面癜く芋えたす。 たずえば、PNGファむルで動䜜する画像を保存およびロヌドする最初のシステムであり、テストデバむスにレベルをロヌドするのに玄15秒かかりたした。 プロファむルされ、敎理されたす-ほずんどの堎合、PNGデコヌドにかかりたした。 このコヌドを曞き盎したした新しい内郚グラフィックスストレヌゞ圢匏が必芁でした-同じテストデバむスで、読み蟌みに6秒かかりたした。 9秒節玄-すべおのゲヌムに新しいストレヌゞシステムを導入したした。 基本的な指暙ずしおゲヌムのダりンロヌドを20回カりントするず、少なくずも50人の呜が救われたようです。 さらに、叀い職堎で同じこずを行った初心者のアドバむスにより、このメカニズムはさらに20〜30加速されたした。これは、ある時点でプロセッサの単玔な蚈算がブヌトシステムのボトルネックでなくなったため、すべおがストレヌゞからの読み取り速床にかかったためです。 圢匏を倉曎したした。

䞀般的に、最適化には非垞に倚くの䜜業がありたす。 ゲヌムは叀いハヌドりェアでも動䜜し、フレヌムワヌクはiOS 4.3をサポヌトしたす珟圚iOS 5アフィリ゚むトコヌドが原因で、その埌、libc ++の䜿甚を開始したした。これは、iOS 5からフレヌムワヌクの2番目のバヌゞョンでも利甚可胜です。 私たちは、トップモデルの完党に新しいアプリケヌションず実隓の開発を行っおいたす。なぜなら、開発の終わりたでに、それらは最も倧芏暡なデバむスになるからです。しかし、「叀いもの」を忘れないでください。 同じCut the Ropeで、ほずんどのリリヌスはコンテンツの曎新です。 叀いコヌドを台無しにしたせん。 新しいゲヌムはすでに芖芚的にはるかに豊かですが、ハヌドりェア芁件はより高くなっおいたす。



プロトタむピングは非垞に迅速に行われ、倚くのスタゞオよりも高速です。 ゲヌムデザむナヌはコンセプトを発衚し、1〜2日で開発者の1人が「倢の仕事」を行いたす。グラフィックスなしでプリミティブからプロトタむプをれロから収集したす。 これがゲヌムデザむナヌを固定した埌にボヌルず正方圢をゞャンプした堎合、圌は仕事を続けたす。 圓然のこずながら、通垞のタスクよりもプロトタむプの数ははるかに少なくなりたすが、チヌムの党員が遅かれ早かれ独自のものを䜜成するよう努めおいたす。



繰り返しになりたすが、もちろん、すべおの基本的なこずがある既補の調達プロゞェクトでこれをすぐに行いたす。 ネむティブモバむルアプリケヌションの開発に携わる人々にずっお、これは単なる別の䞖界です。暙準的なコントロヌラヌはなく、リ゜ヌスの準備は圌ら自身のものです。 Unityで働いおいた人たち-圌らは「ボンネットの䞋」を掘るこずにもっず興味があり、そこで行うのが難しいいく぀かのこずの実装を芋おいたす。 䞀般に、ココナッツには高いレベルで類䌌点がありたすが、ゲヌムを解析し、内郚でどのように機胜するかを芋るのは䟝然ずしお興味深いです。



テストに぀いお少し





最埌に-友達ず私の小さな議論。 以䞋のネタバレの䞋に、さたざたな人々のテストタスクからの5぀のコヌドサンプルがありたす。 コヌドは、すべおの候補者の同意を埗お公開されおいたす。 慎重に、゜ヌスコヌドは非垞に倧きいです



App.cpp
// // App.cpp // Asteroids // // Created by xxxx // // #include <string> #include "App.h" #include "RenderCommandPolygonConvex.h" #include "Vec2.h" #include "Color.h" #include "GameMap.h" #include "Camera.h" #include "MapDrawObjectPolygon.h" #include "MapObjectMovable.h" #include "IMovable.h" #include "MovableObjectTouch.h" #include "MapObjectEmitter.h" #include "EmitterLineContinuous.h" #include "MovableInDirection.h" #include "MapObjectHero.h" #include "MapObjectAsteroid.h" #include "MapObjectDebris.h" const float LOGIC_MAP_WIDTH = 100; const float GAMEPLAY_ACCELERATION = 0.003; namespace { void initAsteroidsEmitters(GameMapPtr gameMap, float logicMapWidth, std::vector<EmitterLineContinuousPtr>& asteroidEmitters) { for_each(asteroidEmitters.begin(), asteroidEmitters.end(), [](EmitterLineContinuousPtr emitter){emitter->die();}); MapObjectEmitterPtr emitterMapObject(new MapObjectEmitter()); EmitterLineContinuousPtr emitter(new EmitterLineContinuous(Vec2(-logicMapWidth*0.5f, 0), Vec2(logicMapWidth*1.5f, 0), Vec2(0, 0), 8, 25, -1, gameMap)); emitter->setParticlesMapObject(MapObjectAsteroidPtr(new MapObjectAsteroid())); asteroidEmitters.push_back(emitter); emitterMapObject->setEmitter(emitter); gameMap->addMapObject(emitterMapObject, Vec2(0, -10), 0); MapObjectEmitterPtr emitterMapObject2(new MapObjectEmitter()); EmitterLineContinuousPtr emitter2(new EmitterLineContinuous(Vec2(0, 0), Vec2(logicMapWidth, 0), Vec2(0, 1), 1, 30, -1, gameMap)); emitter2->setParticlesMapObject(MapObjectAsteroidPtr(new MapObjectAsteroid())); emitterMapObject2->setEmitter(emitter2); asteroidEmitters.push_back(emitter2); gameMap->addMapObject(emitterMapObject2, Vec2(0, -10), 0); MapObjectEmitterPtr emitterMapObject3(new MapObjectEmitter()); EmitterLineContinuousPtr emitter3(new EmitterLineContinuous(Vec2(0, 0), Vec2(logicMapWidth, 0), Vec2(0, 1), 3, 20, -1, gameMap)); emitter3->setParticlesMapObject(MapObjectDebrisPtr(new MapObjectDebris())); emitterMapObject3->setEmitter(emitter3); asteroidEmitters.push_back(emitter3); gameMap->addMapObject(emitterMapObject3, Vec2(0, -10), 0); MapObjectEmitterPtr emitterMapObject4(new MapObjectEmitter()); EmitterLineContinuousPtr emitter4(new EmitterLineContinuous(Vec2(0, 0), Vec2(logicMapWidth, 0), Vec2(0, 1), 1, 40, -1, gameMap)); emitter4->setParticlesMapObject(MapObjectAsteroidPtr(new MapObjectAsteroid())); emitterMapObject4->setEmitter(emitter4); asteroidEmitters.push_back(emitter4); gameMap->addMapObject(emitterMapObject4, Vec2(0, -10), 0); } } App::App() :time_(0) { } void App::updateAndRender(float dtSec, std::vector<RenderCommandBasePtr>& renderCommands) { update(dtSec); collectRenderData(renderCommands); } bool App::touch(const std::vector<TouchEvent>& events) const { if (events.empty()) return false; if (gameMap_) gameMap_->touch(events); return true; } void App::update(float dtSec) { if (gameMap_) gameMap_->update(dtSec); tryRespawnHero(); updateGameplayAcceleration(); time_+= dtSec; } void App::resetGameplay() { time_ = 0; ::initAsteroidsEmitters(gameMap_, LOGIC_MAP_WIDTH, asteroidEmitters_); } void App::tryRespawnHero() { if (!hero_ || hero_->isReadyToDestruct() ) { if (!gameMap_->hasObjectOfType(MAP_OBJECT_HERO_DEBRIS)) { resetGameplay(); hero_ = MapObjectHeroPtr(new MapObjectHero(Rect(0, 0, logicMapSize_.x, logicMapSize_.y))); gameMap_->addMapObject(hero_, Vec2(50, logicMapSize_.y - 10), 0); } } } void App::setScreenSize(int screenW, int screenH) { screenSize_ = Vec2(screenW, screenH); float logicCellSize = screenW/LOGIC_MAP_WIDTH; logicMapSize_ = Vec2(screenW/logicCellSize, screenH/logicCellSize); CameraPtr camera = CameraPtr(new Camera(logicCellSize, logicCellSize)); gameMap_ = GameMapPtr(new GameMap(Size(logicMapSize_.x, logicMapSize_.y), camera)); gameMap_->setLiveAreaRect(Rect(-logicMapSize_.x/2, -10, logicMapSize_.x*2, logicMapSize_.y + 20)); resetGameplay(); } void App::collectRenderData(std::vector<RenderCommandBasePtr>& renderCommands) const { gameMap_->collectRenderData(renderCommands); } void App::updateGameplayAcceleration() { for (auto emitter: asteroidEmitters_) { emitter->setSpeedParticles(emitter->getSpeedParticles() + time_*GAMEPLAY_ACCELERATION); } }
      
      







game.cpp
 /* w,a,d    r   , space  */ #include "stdafx.h" #include <math.h> #include <vector> #include <iostream> #include <fstream> #include <glut.h> using namespace std; const float Pi=3.14159265358; float winwid=400; float winhei=400; bool game_end=0; /////bullet//// float dx=0,dy=0; float bull_speed=6; float betta=0; bool fl1=0, fl2=0; /////ship//// float speed=0; float angle=0; float acsel=0; /////asteroid///// float ast_size=50; float aster_speed=3; /////0-rand//// int kol_aster=0; class bullet { public: float dxb; float dyb; float angleb; bullet() { dxb=dx; dyb=dy; angleb=betta; } }; class asteroid { public: float anglea; float dx; float dy; float depth; int n; int i_big; int ifsmall; vector <double> x; vector <double> y; void create(int i,bool param); void create_small(int i,int j,bool param,float depth1,float dx1,float dy1); }; void asteroid:: create_small(int i,int j,bool param,float depth1,float dx1,float dy1) { ifsmall=0; int size=ast_size/2; depth=depth1+(j+2)*1.0/(8.0*(kol_aster)); dx=dx1; dy=dy1; i_big=i; ///////////////////////////////////////////////// int quat=rand()%4; int n1=rand()%2+1; int n2=rand()%2+1; int n3=rand()%2+1; int n4=rand()%2+1; n1=n2=n3=n4=1; n=n1+n2+n3+n4; double xi,yi; anglea=rand()%360; x.clear(); y.clear(); for (int i=0;i<n1;i++) { xi=rand()%(size/2)-size/2; yi=rand()%(size/2)+size/2; x.push_back(xi); y.push_back(yi); } for (int i=0;i<n2;i++) { xi=rand()%(size/2)+size/2; yi=rand()%(size/2)+size/2; x.push_back(xi); y.push_back(yi); } for (int i=0;i<n3;i++) { xi=rand()%(size/2)+size/2; yi=rand()%(size/2)-size/2; x.push_back(xi); y.push_back(yi); } for (int i=0;i<n4;i++) { xi=rand()%(size/2)-size/2; yi=rand()%(size/2)-size/2; x.push_back(xi); y.push_back(yi); } ////////////////////////////////////////////////// } void asteroid:: create(int kol_exist,bool param) { int size=ast_size; int quat=rand()%4; int n1=rand()%2+1; int n2=rand()%2+1; int n3=rand()%2+1; int n4=rand()%2+1; n1=n2=n3=n4=1; n=n1+n2+n3+n4; double xi,yi; anglea=rand()%360; i_big=kol_exist; ifsmall=1; depth=(float)(kol_exist)/((kol_aster)); dx=rand()%(int)winwid -winwid/2; dy=rand()%(int)winhei -winhei/2; if(quat==0) dy=-ast_size-winhei/2; if(quat==1) dy=ast_size+winhei/2; if(quat==2) dx=-ast_size-winwid/2; if(quat==3) dx=ast_size+winwid/2; x.clear(); y.clear(); for (int i=0;i<n1;i++) { xi=rand()%(size/2)-size/2; yi=rand()%(size/2)+size/2; x.push_back(xi); y.push_back(yi); } for (int i=0;i<n2;i++) { xi=rand()%(size/2)+size/2; yi=rand()%(size/2)+size/2; x.push_back(xi); y.push_back(yi); } for (int i=0;i<n3;i++) { xi=rand()%(size/2)+size/2; yi=rand()%(size/2)-size/2; x.push_back(xi); y.push_back(yi); } for (int i=0;i<n4;i++) { xi=rand()%(size/2)-size/2; yi=rand()%(size/2)-size/2; x.push_back(xi); y.push_back(yi); } } vector <bullet> vecb; vector <asteroid> veca; void destroy_small_ast( int i) { //////  4 -  ///// bool create_big=1; float up_boarder=(float)(veca[i].i_big)/((kol_aster)); float down_boarder=(float)(veca[i].i_big)/((kol_aster)); asteroid a_big; a_big.create(veca[i].i_big,1); if (i>0) if(veca[i-1].depth>down_boarder) create_big=0; if (i<veca.size()-1) if(veca[i+1].depth<up_boarder) create_big=0; {if (create_big==1) {veca.insert(veca.begin()+veca[i].i_big,a_big); veca[veca[i].i_big].create(veca[i].i_big,1); veca.erase(veca.begin()+i+1);} else veca.erase(veca.begin()+i);} } void destroy_aster(float dep) { dep=1-2*dep; for(int i=0;i<veca.size();i++) { if (abs(dep-veca[i].depth)<0.0001) {if(veca[i].ifsmall==1) { veca.resize(veca.size()+4); for(int j=0;j<4;j++) veca[veca.size()-j-1].create_small(i,j,0,veca[i].depth,veca[i].dx,veca[i].dy); veca.erase(veca.begin()+i); break; } else {destroy_small_ast( i);break;} } } } void shoot() { float depth[5]; for(int i=0;i<vecb.size();i++) { glLoadIdentity(); ////// ////// vecb[i].dxb+=(speed+bull_speed)*cos(Pi*(vecb[i].angleb)/180.0); vecb[i].dyb+=(speed+bull_speed)*sin(Pi*(vecb[i].angleb)/180.0); ////  ///// if((vecb[i].dxb>winwid/2-1) ||(vecb[i].dxb<-winwid/2+1) ||(vecb[i].dyb<-winhei/2+1) || (vecb[i].dyb>winhei/2-1)) {vecb.erase(vecb.begin()+i);i--;} else{ ///// //// glReadPixels((vecb[i].dxb+winwid/2),-vecb[i].dyb+winhei/2,2,2,GL_DEPTH_COMPONENT,GL_FLOAT,depth); if (depth[0]!=1) { destroy_aster(depth[0]); vecb.erase(vecb.begin()+i); i--; } else { ////// ////// glTranslatef(vecb[i].dxb,vecb[i].dyb,0.0f); glColor3f(1.0f,1.0f,1.0f); glBegin(GL_LINES); glVertex3f( 0.0,0.0, 0.5f); glVertex3f(1.0,0.0, 0.5f); glEnd(); } } } } void aster_draw() { glColor3f(0.5f,1.0f,1.0f); glLoadIdentity(); for (int i=0;i<veca.size();i++) { glBegin(GL_POLYGON); for(int j=0;j<veca[i].n;j++) glVertex3f( veca[i].dx+veca[i].x[j],veca[i].dy+veca[i].y[j], veca[i].depth); glEnd(); veca[i].dx+=aster_speed*cos(Pi*(veca[i].anglea)/180.0); veca[i].dy+=aster_speed*sin(Pi*(veca[i].anglea)/180.0); /////    ,   /// if((veca[i].dx>winwid/2+ast_size) ||(veca[i].dx<-winwid/2-ast_size) ||(veca[i].dy<-winhei/2-ast_size) || (veca[i].dy>winhei/2+ast_size)) if (veca[i].ifsmall==0) {destroy_small_ast( i);i--;} else veca[i].create(i,1); } } void asteroidsinit() { int k; k=rand()%6+4; if(kol_aster!=0) k=kol_aster; else kol_aster=k; veca.resize(k); for(int i=0;i<k;i++) veca[i].create(i,1); } void draw_ship() { float depth[6]; float dx1,dx2,dy1,dy2; ////////   ///// if ((dx<winwid/2-1)&&(dx>-winwid/2+1)&&(dy<winwid/2-1)&&(dy>-winwid/2+1)) glReadPixels((dx+winwid/2),-dy+winhei/2,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,depth); else depth[0]=1; dx1=dx-10*cos(Pi*betta/180)-10*sin(Pi*betta/180)+winwid/2; dy1=-dy+10*sin(Pi*betta/180)-10*cos(Pi*betta/180)+winhei/2; if ((dx1<winwid-1)&&(dx1>0+1)&&(dy1<winhei-1)&&(dy1>0+1)) glReadPixels(dx1,dy1,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,depth+1); else depth[1]=1; dx2=dx-10*cos(Pi*betta/180)+10*sin(Pi*betta/180)+winwid/2; dy2=-dy+10*sin(Pi*betta/180)+10*cos(Pi*betta/180)+winhei/2; if ((dx2<winwid-1)&&(dx2>0+1)&&(dy2<winhei-1)&&(dy2>0+1)) glReadPixels(dx2,dy2,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,depth+2); else depth[2]=1; ///////   //// if(dx>winwid/2) dx=-winwid/2; if(dx<-winwid/2) dx=winwid/2; if(dy<-winhei/2) dy=winhei/2; if(dy>winhei/2) dy=-winhei/2; /////////////// glColor3f(0.8f,0.0f,0.8f); glLoadIdentity(); glTranslatef(dx,dy,0.0f); glRotatef(betta,0.0f,0.0f,1.0f); glBegin(GL_TRIANGLES); glVertex3f( -10.0f,-10.0f, 1.0f); glVertex3f(-10.0f,10.0f, 1.0f); glVertex3f(0.0f,0.0f, 1.0f); if (fl2==1){ glVertex3f( -10.0f,-3.0f, 1.0f); glVertex3f(-10.0f,3.0f, 1.0f); glVertex3f(-15.0f,0.0f, 1.0f); } glEnd(); ///////// - ///////// if ((depth[0]!=1)||(depth[1]!=1)||(depth[2]!=1)) game_end=1; } void display() { glClearDepth( 1.0f ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); aster_draw(); draw_ship(); shoot(); glutSwapBuffers(); } void Timer(int) { acsel--; if(speed>10) speed=10; if (fl1==1) {angle=betta;fl1=0;} if (acsel==0) {fl2=0;} dx=dx+speed*cos(Pi*angle/180.0); dy=dy+speed*sin(Pi*angle/180.0); if(speed>0)speed=speed-0.1; else speed=0; display(); if(game_end==0) glutTimerFunc(50,Timer,0); } void Initialize() { dx=0; dy=0; vecb.empty(); angle=betta=speed=0; glClearColor(0, 0, 0.0, 1.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-winwid/2, winwid/2, winhei/2, -winhei/2, -1, 1); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glDepthFunc( GL_LEQUAL ); float depth[5]; glClearDepth( 1.0f ); //     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); asteroidsinit(); glutTimerFunc(500,Timer,0); } void keyboard(unsigned char key,int x,int y) { if (key=='w') {fl1=1;speed++;fl2=1;acsel=10;} if (key=='d') {betta+=7;} if (key=='a') betta-=7; if (key==' ') {bullet b1;vecb.push_back(b1);} if(key=='r') {if(game_end==1) {game_end=0;Initialize();}} } int main(int argc, char **argv)//  { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH |GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(winwid, winhei); glutInitWindowPosition(200, 200); glutCreateWindow("Powder Toy"); Initialize(); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMainLoop(); }
      
      







game.cpp
 #include "game.h" #include "logic.h" Game::Game(unsigned width, unsigned height) : _asteroids(std::vector<AsteroidFamily *>()), _shots(std::vector<Shot *>()), _booms(std::vector<Boom *>()), _score(0), _livesBonus(10000), _level(0), _isAsteroidsEmpty(true), _gameOver(false), _playerPoints(new std::vector<Point>(3)), _shotsPoints(new std::vector<Point>(4)), _boomPoints(new std::vector<Point>(8)), _lastTimepoint(std::chrono::high_resolution_clock::now()), _lastShotTimepoint(std::chrono::high_resolution_clock::now()), _gameOverTimepoint(std::chrono::high_resolution_clock::now()), _timeMultiplier(0.0f), _width(width), _height(height), _aspectRatio((float)width / (float)height), _render(new GL(&_aspectRatio, &_halfWidth, &_halfHeight)), _controls(new Controls(&_width, &_height, &_halfWidth, &_halfHeight)) { _halfHeight = GAME_HEIGHT; _halfWidth = _aspectRatio * _halfHeight; _playerPoints = new std::vector<Point>(3); (*_playerPoints)[0].x = -0.6f; (*_playerPoints)[0].y = -0.5f; (*_playerPoints)[1].x = -0.6f; (*_playerPoints)[1].y = 0.5f; (*_playerPoints)[2].x = 0.6f; (*_playerPoints)[2].y = 0.0f; _shotsPoints = new std::vector<Point>(4); (*_shotsPoints)[0].x = 0.02f; (*_shotsPoints)[0].y = 0.02f; (*_shotsPoints)[1].x = 0.02f; (*_shotsPoints)[1].y = -0.02f; (*_shotsPoints)[2].x = -0.02f; (*_shotsPoints)[2].y = -0.02f; (*_shotsPoints)[3].x = -0.02f; (*_shotsPoints)[3].y = 0.02f; _boomPoints = new std::vector<Point>(8); (*_boomPoints)[0].x = 0.1f; (*_boomPoints)[0].y = 0.1f; (*_boomPoints)[1].x = 0.5f; (*_boomPoints)[1].y = 0.4f; (*_boomPoints)[2].x = -0.1f; (*_boomPoints)[2].y = -0.2f; (*_boomPoints)[3].x = -0.5f; (*_boomPoints)[3].y = -0.4f; (*_boomPoints)[4].x = 0.2f; (*_boomPoints)[4].y = -0.1f; (*_boomPoints)[5].x = 0.5f; (*_boomPoints)[5].y = -0.5f; (*_boomPoints)[6].x = -0.1f; (*_boomPoints)[6].y = 0.2f; (*_boomPoints)[7].x = -0.5f; (*_boomPoints)[7].y = 0.5f; Shot::SetStaticPoints(_shotsPoints); Boom::SetStaticPoints(_boomPoints); Random::Init(&_halfWidth, &_halfHeight); _player = new Player(_playerPoints, 0.0f, 0.0f); } Game::~Game() { delete _player; delete _render; for (Shot *item : _shots) delete item; for (Boom *item : _booms) delete item; for (AsteroidFamily *item : _asteroids) delete item; delete _playerPoints; delete _shotsPoints; delete _boomPoints; } void Game::Refresh() { _controls->Refresh(); if (_score >= _livesBonus) { _livesBonus += 10000; _player->SetLives(_player->GetLives() + 1); } if (_isAsteroidsEmpty) { _level++; for (AsteroidFamily *item : _asteroids) delete item; _asteroids.clear(); for (unsigned i = 0; i < (_level + 1) * 2; ++i) _asteroids.push_back(Random::GenerateAsteroidFamily()); _isAsteroidsEmpty = false; } _isAsteroidsEmpty = true; std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); auto time_span = std::chrono::duration_cast<std::chrono::nanoseconds>(now - _lastTimepoint).count(); _timeMultiplier = (float)time_span / 16666666.67f; _lastTimepoint = now; if (_controls->GetHyperspace()) { if (!_gameOver) { Random::ChangePlayerCoords(_player); _controls->SetHyperspace(false); } else { if (std::chrono::duration_cast<std::chrono::milliseconds>(now - _gameOverTimepoint).count() >= GAMEOVER_SCORE_TIME) { _player->SetCoord(0.0f, 0.0f); _player->SetAngle(0.0f); _player->SetLives(PLAYER_DEFAULT_LIVES); for (AsteroidFamily *item : _asteroids) delete item; _asteroids.clear(); _isAsteroidsEmpty = true; _score = 0; _level = 0; _livesBonus = 10000; _gameOver = false; _controls->SetHyperspace(false); } } } if (_player->GetIsGhost() && !_gameOver) { if (!_player->GetIsRendering()) { if (std::chrono::duration_cast<std::chrono::milliseconds>(now - _player->GetDeadTime()).count() >= PLAYER_BLACKOUT_TIME) { _player->SetIsRendering(true); } } if (std::chrono::duration_cast<std::chrono::milliseconds>(now - _player->GetDeadTime()).count() >= PLAYER_GHOST_TIME) { _player->SetIsGhost(false); } } if (_player->GetLives() <= 0 && !_gameOver) { _gameOver = true; _gameOverTimepoint = now; _player->SetIsGhost(true); _player->Stop(); } if (_player->GetIsRendering() && !_gameOver) { RefreshObjectCoord(_player); _player->Refresh(_controls->GetAngle(), _controls->GetAcceleration()); if (_controls->GetShoot()) { if (std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastShotTimepoint).count() >= NEXT_SHOT_TIME) { _shots.push_back(_player->GenerateShot()); _lastShotTimepoint = now; } } } for (auto it = _shots.begin(); it != _shots.end();) { RefreshObjectCoord(*it); (*it)->Refresh(_timeMultiplier); if ((*it)->GetDistance() >= std::min(_halfHeight, _halfWidth) * 2 - 1.2f) { delete(*it); it = _shots.erase(it); } else { ++it; } } for (AsteroidFamily *item : _asteroids) { if (item->GetLarge()->GetIsRendering()) { _isAsteroidsEmpty = false; RefreshObjectCoord(item->GetLarge()); item->GetLarge()->Refresh(); if (!_player->GetIsGhost()) { if (isCollision(_player, item->GetLarge())) { item->DestroyLarge(); _score += SCORE_LARGE; ProcessCollision(_player, item->GetLarge()); } } if (item->GetLarge()->GetIsRendering()) for (auto it = _shots.begin(); it != _shots.end();) { if (isCollision(*it, item->GetLarge())) { delete(*it); it = _shots.erase(it); item->DestroyLarge(); _booms.push_back(new Boom(item->GetLarge()->GetCoord().x, item->GetLarge()->GetCoord().y)); _score += SCORE_LARGE; } else { ++it; } } } else { if (item->GetFirstSmall()->GetIsRendering()) { _isAsteroidsEmpty = false; RefreshObjectCoord(item->GetFirstSmall()); item->GetFirstSmall()->Refresh(); if (!_player->GetIsGhost()) { if (isCollision(_player, item->GetFirstSmall())) { item->GetFirstSmall()->SetIsRendering(false); _score += SCORE_SMALL; ProcessCollision(_player, item->GetFirstSmall()); } } } if (item->GetSecondSmall()->GetIsRendering()) { _isAsteroidsEmpty = false; RefreshObjectCoord(item->GetSecondSmall()); item->GetSecondSmall()->Refresh(); if (!_player->GetIsGhost()) { if (isCollision(_player, item->GetSecondSmall())) { item->GetSecondSmall()->SetIsRendering(false); _score += SCORE_SMALL; ProcessCollision(_player, item->GetSecondSmall()); } } } for (auto it = _shots.begin(); it != _shots.end();) { bool isFirstCollision = false, isSecondCollision = false; if (item->GetFirstSmall()->GetIsRendering()) isFirstCollision = isCollision(*it, item->GetFirstSmall()); if (item->GetSecondSmall()->GetIsRendering()) isSecondCollision = isCollision(*it, item->GetSecondSmall()); if (isFirstCollision || isSecondCollision) { delete(*it); it = _shots.erase(it); if (isFirstCollision) { item->GetFirstSmall()->SetIsRendering(false); _booms.push_back(new Boom(item->GetFirstSmall()->GetCoord().x, item->GetFirstSmall()->GetCoord().y)); _score += SCORE_SMALL; } if (isSecondCollision) { item->GetSecondSmall()->SetIsRendering(false); _booms.push_back(new Boom(item->GetSecondSmall()->GetCoord().x, item->GetSecondSmall()->GetCoord().y)); _score += SCORE_SMALL; } } else { ++it; } } } } for (auto it = _booms.begin(); it != _booms.end();) { (*it)->Refresh(_timeMultiplier); if ((*it)->GetDuration() >= BOOM_MAX_DURATION) { delete(*it); it = _booms.erase(it); } else { ++it; } } } void Game::RefreshObjectCoord(Object *object) { object->SetCoord(object->GetCoord().x + object->GetVelocity().x * _timeMultiplier, object->GetCoord().y + object->GetVelocity().y * _timeMultiplier); if (object->GetCoord().x <= -_halfWidth) object->SetCoord(object->GetCoord().x + _halfWidth * 2, object->GetCoord().y); else if (object->GetCoord().x >= _halfWidth) object->SetCoord(object->GetCoord().x - _halfWidth * 2, object->GetCoord().y); if (object->GetCoord().y <= -_halfHeight) object->SetCoord(object->GetCoord().x, object->GetCoord().y + _halfHeight * 2); else if (object->GetCoord().y >= _halfHeight) object->SetCoord(object->GetCoord().x, object->GetCoord().y - _halfHeight * 2); } void Game::Render() { Refresh(); _render->Clear(); _render->RenderControls(_controls); _render->SetColor(OBJECTS_COLOR); if (_player->GetIsRendering() && !_gameOver) { if (_player->GetIsGhost()) _render->SetColor(PLAYER_GHOST_COLOR); _render->RenderPlayer(_player); } if (_gameOver) _render->SetColor(OBJECTS_GAMEOVER_COLOR); else _render->SetColor(OBJECTS_COLOR); for (AsteroidFamily *item : _asteroids) { if (item->GetLarge()->GetIsRendering()) { _render->RenderAsteroid(item->GetLarge()); } else { if (item->GetFirstSmall()->GetIsRendering()) { _render->RenderAsteroid(item->GetFirstSmall()); } if (item->GetSecondSmall()->GetIsRendering()) { _render->RenderAsteroid(item->GetSecondSmall()); } } } for (Shot *item : _shots) { _render->RenderShot(item); } _render->SetColor(BOOM_COLOR); for (Boom *item : _booms) { _render->RenderBoom(item); } _render->SetColor(TEXT_COLOR); if (!_gameOver) { _render->RenderScoreAndLives(_score, _player->GetLives()); } else { _render->RenderGameOver(_score); } } bool Game::isCollision(Player *player, Asteroid *asteroid) { const std::vector<Point> &playerPoints = *(player->GetPoints()); const std::vector<Point> &asteroidPoints = *(asteroid->GetPoints()); if (TestAABB(player, asteroid)) { for (unsigned i = 0; i < playerPoints.size(); i++) { for (unsigned j = 0; j < asteroidPoints.size(); j++) { if (Logic::IsLinesCross(playerPoints[i], playerPoints[(i + 1 == playerPoints.size()) ? 0 : i + 1], asteroidPoints[j], asteroidPoints[(j + 1 == asteroidPoints.size()) ? 0 : j + 1])) { return true; } } } if (Logic::IsInside(asteroidPoints, playerPoints[0])) { return true; } } return false; } bool Game::isCollision(Shot *shot, Asteroid *asteroid) { if (Logic::IsInside(*(asteroid->GetPoints()), shot->GetCoord())) return true; else return false; } bool Game::TestAABB(Player *player, Asteroid *asteroid) { return (player->GetSizes()[0] < asteroid->GetSizes()[1] && player->GetSizes()[1] > asteroid->GetSizes()[0] && player->GetSizes()[2] < asteroid->GetSizes()[3] && player->GetSizes()[3] > asteroid->GetSizes()[2]); } void Game::ProcessCollision(Player *player, Asteroid *asteroid) { _booms.push_back(new Boom(asteroid->GetCoord().x, asteroid->GetCoord().y)); player->SetIsRendering(false); player->SetIsGhost(true); player->SetLives(player->GetLives() - 1); player->SetCoord(0.0f, 0.0f); player->SetDeadTime(std::chrono::high_resolution_clock::now()); } void Game::Resize(float width, float height) { _aspectRatio = (float)width / (float)height; _halfWidth = _aspectRatio * _halfHeight; _width = width; _height = height; _render->Resize(); } Controls *Game::GetControls() { return _controls; } bool Game::GetIsPaused() { return _isPaused; } void Game::SetIsPaused(bool isPaused) { _isPaused = isPaused; }
      
      







Game.cpp
 #include <Game.h> #include <Controls.h> #include <tuple> Game::Game() { isLevelRunning = true; ResetLogic(); RequestRestart(); Renderer::InitInternals(); Controls::Init(); Score::Init(); } Game& Game::Get() { static Game instance; return instance; } void Game::Restart() { objects.clear(); Score::OnRestart(); ResetLogic(); GameObject::Create<Ship>(); SpawnAsteroids(Constant::asteroidTargetCount); } //  ,       (false -  ) bool Game::IsLevelRunning(float dt) { if(wantRestart) { if(restartTimer < 0.0) { if(isLevelRunning) { Restart(); wantRestart = false; } } else { restartTimer -= dt; } } return isLevelRunning; } void Game::Update() { float deltaTime = timer.Tick(); if(IsLevelRunning(deltaTime)) { for(auto& go : objects) { go->Update(deltaTime); } DetectCollisions(deltaTime); DestroyRequestedObjects(); //     } Renderer::Draw(); } void Game::OnGLInit() { Renderer::InitGLContext(); } void Game::OnResolutionChange(int w, int h) { Renderer::OnResolutionChange(w, h); Controls::Resize(); Score::Resize(); } GameObject& Game::AddGameObject(std::unique_ptr<GameObject> obj) { objects.push_back(std::move(obj)); return *objects.back().get(); } void Game::DestroyRequestedObjects() { for(auto i = objects.begin(); i != objects.end();) { if((*i)->isDestructionRequested()) { i = objects.erase(i); } else { ++i; } } } void Game::DetectCollisions(float dt) { for(auto a = objects.begin(); a != objects.end(); ++a) { for(auto b = std::next(a); b != objects.end(); ++b) { //    GameObject& ra = **a; GameObject& rb = **b; if(CollisionMask(ra, rb)) { //    if(DetectCollision(ra, rb, dt)) { //  if(RefineCollision(ra, rb, dt)) { //  ra.OnCollision(rb); rb.OnCollision(ra); } } } } } } bool Game::CollisionMask(const GameObject& a, const GameObject& b) { //      ,       (return false) return !(a.isDestructionRequested() || b.isDestructionRequested()) && //          ,   false (a.CollisionMask(b.getStaticType()) || b.CollisionMask(a.getStaticType())); } //    ,    //      ,       dt bool Game::DetectCollision(const GameObject& a, const GameObject& b, float dt) { if(Constant::continuousCollisions) { return (a.getPosition() - b.getPosition()).getLength() < a.getRadius() + b.getRadius() + (a.getVelocity().getLength() + b.getVelocity().getLength()) * dt; } else { return (a.getPosition() - b.getPosition()).getLength() < a.getRadius() + b.getRadius(); } } bool Game::RefineCollision(const GameObject& a, const GameObject& b, float dt) { if(Constant::refineCollisions) { const Model& am = a.getModel(); const std::vector<GLfloat>& av = am.getTransformed(); const std::vector<GLubyte>& ai = am.getIndices(); const Vec2 aBackVel = a.getVelocity() * -1; const Model& bm = b.getModel(); const std::vector<GLfloat>& bv = bm.getTransformed(); const std::vector<GLubyte>& bi = bm.getIndices(); const Vec2 bBackVel = b.getVelocity() * -1; //    ,      for(int i = 0; i < ai.size(); i += 2) { Vec2 a0(i, av, ai); Vec2 at = Vec2(i + 1, av, ai) - a0; for(int j = 0; j < bi.size(); j += 2) { Vec2 b0(j, bv, bi); Vec2 bt = Vec2(j + 1, bv, bi) - b0; if(Constant::continuousCollisions ? // ,       t0, //     t0+dt,      - MovingSegmentCollision(a0, at, aBackVel, b0, bt, bBackVel, dt) : SegmentCollision(a0, at, b0, bt)) return true; } } return false; } else { return true; } } bool Game::SegmentCollision(Vec2 p, Vec2 r, Vec2 q, Vec2 s) { //http://stackoverflow.com/a/565282/2502024 float det = Vec2::CrossProd2D(r, s); if(fabs(det) > Constant::smallNumber) { Vec2 diff = q - p; float f = Vec2::CrossProd2D(diff, s / det); float g = Vec2::CrossProd2D(diff, r / det); return f >= 0 && f <= 1 && g >= 0 && g <= 1; } return false; } bool Game::MovingSegmentCollision(Vec2 p, Vec2 r, Vec2 vp, Vec2 q, Vec2 s, Vec2 vq, float dt) { float det = Vec2::CrossProd2D(r, s); if(fabs(det) > Constant::smallNumber) { const Vec2 v = vq - vp; const Vec2 diff = q - p; //    : //q = q0 + v*t, t in [0, dt] //(vx * sy - vy * sx) * t + ((qx - px) * sy - (qy - py) * sx) //  f  g  SegmentCollision    t. //    t  [0, dt],    t, // f  g    [0, 1]. ..  3    t auto getInequation = [=](Vec2 dir)->std::tuple<float, float> { //0 <= a*t + cp <= 1 float cp = Vec2::CrossProd2D(diff, dir / det); float a = Vec2::CrossProd2D(v, dir / det); float left = -cp, right = 1 - cp; if(fabs(a) < Constant::smallNumber) { if(cp >= 0 && cp <= 1) { left = 0; right = dt; } else { left = dt + 1; right = -1; } } else { left /= a; right /= a; if(a < 0) std::swap(left, right); } return std::make_tuple(left, right); }; float ls, rs, lr, rr; //ls <= t <= rs std::tie(ls, rs) = getInequation(s); //lr <= t <= rr std::tie(lr, rr) = getInequation(r); //  0 <= t <= dt float mx = std::max(0.f, std::max(ls, lr)); float mn = std::min(dt, std::min(rs, rr)); return mx <= mn; } return false; } void Game::RequestRestart(float t) { wantRestart = true; restartTimer = t; } void Game::Pause() { isLevelRunning = false; Controls::onPause(); } void Game::Resume() { isLevelRunning = true; Controls::onResume(); timer.Tick(); } void Game::SetPlayerPos(const Ship& player) { playerPos = player.getPosition(); } Vec2 Game::GetPlayerPos() { return playerPos; } void Game::AddPoints(int pointsToAdd) { Score::AddPoints(pointsToAdd); } void Game::DecAsteroidCount(const Asteroid& a) { asteroidCount--; if(asteroidCount == Constant::asteroidUfoCount * 2 && !isUfoPresent) { GameObject::Create<UFO>(GetUfoSpawn()); } if(asteroidCount <= Constant::asteroidRespawnCount * 2) { SpawnAsteroids(Constant::asteroidTargetCount - Constant::asteroidRespawnCount); } } //  2, .. asteroidCount      void Game::IncAsteroidCount(const Asteroid& a) { asteroidCount += 2; } void Game::ResetLogic() { asteroidCount = 0; isUfoPresent = false; } void Game::SpawnAsteroids(int n) { for(int i = 0; i < n; ++i) { GameObject::Create<Asteroid>(GetSpawnPosition()); } } //  ,      (    ) Transform Game::GetSpawnPosition() { std::uniform_real_distribution<float> zone(-Constant::asteroidSpawnZone, Constant::asteroidSpawnZone); Vec2 pos(Constant::worldRatio + 0.2, 1.2); if(fabs(playerPos.x) > Constant::asteroidSpawnZone && fabs(playerPos.y) < Constant::asteroidSpawnZone) { pos.y = zone(Random::generator); } else if(fabs(playerPos.x) < Constant::asteroidSpawnZone && fabs(playerPos.y) > Constant::asteroidSpawnZone) { pos.x = zone(Random::generator); } else { if(Random::flipCoin()) { pos.x = zone(Random::generator); } else { pos.y = zone(Random::generator); } } return Transform(pos); } Transform Game::GetUfoSpawn() { std::uniform_real_distribution<float> zone(-Constant::ufoZone, Constant::ufoZone); return Transform((Constant::worldRatio + 0.12) * (playerPos.x > 0 ? -1 : 1), zone(Random::generator)); } void Game::OnUfoCreated(const UFO& u) { isUfoPresent = true; } void Game::OnUfoDestroyed(const UFO& u) { isUfoPresent = false; }
      
      







model_handler.cpp
 #include "model_handler.h" #include <time.h> #include <math.h> #include <cstdlib> using namespace model; namespace { const double tickTime = 40.00; const unsigned int asteroidNumber = 8; const float projectileSpeed = 10.0; const float smallAsteroidRadiusK = 1.5; const float minLargeAsteroidRadius = 35.0; const float maxLargeAsteroidRadius = minLargeAsteroidRadius * smallAsteroidRadiusK - 1.0; const float minAsteroidSpeed = 1.5; const float maxAsteroidSpeed = 6.5; const float explosionK = 50000.0; } ModelHandler::ModelHandler(float worldWidth, float worldHeight): _isGameOver(false), _worldWidth(worldWidth), _worldHeight(worldHeight), _tickTime(0), _ship(ShipPtr(new Ship(Point(worldWidth / 2.0, worldHeight / 2.0)))) { srand(time(0)); } void ModelHandler::newGame() { _asteroids.clear(); _projectiles.clear(); _ship.reset(new Ship(Point(_worldWidth / 2.0, _worldHeight / 2.0))); _isGameOver = false; } bool ModelHandler::isGameOver() const { return _isGameOver; } void ModelHandler::update(double deltaTime) { if (this->isGameOver()) return; _tickTime += deltaTime; while (_tickTime > tickTime) { if (_asteroids.size() < ::asteroidNumber) { this->addAsteroid(); } for (ObjectPtr& obj: this->allObjects()) { obj->move(); } this->checkObjects(&_asteroids); this->checkObjects(&_projectiles); if (!this->withinBoundaries(_ship)) { this->removeShip(); } _tickTime -= tickTime; } } ShipPtr ModelHandler::ship() const { return _ship; } void ModelHandler::removeShip() { _isGameOver = true; } void ModelHandler::removeAsteroid(const AsteroidPtr& asteroid) { if (asteroid->collisionRadius() >= ::minLargeAsteroidRadius) { const model::Vector v1(asteroid->velocity() + model::Vector(-asteroid->velocity().y, asteroid->velocity().x)); const model::Vector v2(asteroid->velocity() + model::Vector(asteroid->velocity().y, -asteroid->velocity().x)); this->addAsteroid(asteroid, v1); this->addAsteroid(asteroid, v2); } _asteroids.remove(asteroid); } void ModelHandler::addAsteroid(const AsteroidPtr& asteroid) { _asteroids.push_back(asteroid); } std::list<AsteroidPtr> ModelHandler::asteroids() const { return _asteroids; } std::list<ProjectilePtr> ModelHandler::projectiles() const { return _projectiles; } void ModelHandler::removeProjectile(const ProjectilePtr& projectile) { _projectiles.remove(projectile); } void ModelHandler::addProjectile() { Vector projectileSpeed = _ship->direction() * ::projectileSpeed + ship()->velocity(); _projectiles.push_back(ProjectilePtr(new Projectile(_ship->point(), projectileSpeed))); } void ModelHandler::processHit(const ProjectilePtr& projectile, const AsteroidPtr& asteroid) { if (asteroid->collisionRadius() >= ::minLargeAsteroidRadius) { const Vector ox = asteroid->velocity().normaVector(); Vector explosionVector(ox.y, -ox.x); if (ox * asteroid->velocity() < 0) { explosionVector = explosionVector * (-1.0); } const Vector v1(asteroid->velocity() + model::Vector(-asteroid->velocity().y, asteroid->velocity().x) + explosionVector * (-::explosionK / asteroid->mass())); const Vector v2(asteroid->velocity() + model::Vector(asteroid->velocity().y, -asteroid->velocity().x) + explosionVector * (::explosionK / asteroid->mass())); this->addAsteroid(asteroid, v1); this->addAsteroid(asteroid, v2); } _projectiles.remove(projectile); _asteroids.remove(asteroid); } std::list<ObjectPtr> ModelHandler::allObjects() const { std::list<ObjectPtr> objects; objects.insert(objects.end(), _asteroids.begin(), _asteroids.end()); objects.insert(objects.end(), _projectiles.begin(), _projectiles.end()); objects.push_back(_ship); return objects; } void ModelHandler::addAsteroid() { const float r = common::rangeRand(::minLargeAsteroidRadius, ::maxLargeAsteroidRadius); Point point; bool isCorrect = false; while (!isCorrect) { point = randPoint(r); isCorrect = true; for (const AsteroidPtr& asteroid : _asteroids) { const float d = model::distance(asteroid->point(), point); if (d < (asteroid->collisionRadius() + r)) { isCorrect = false; break; } } } const float distToCenter = ::distance(point.x, point.y, _worldWidth / 2.0, _worldHeight / 2.0); const float vx = (_worldWidth / 2.0 - point.x) / distToCenter * common::rangeRand(::minAsteroidSpeed, ::maxAsteroidSpeed); const float vy = (_worldHeight / 2.0 - point.y) / distToCenter * common::rangeRand(::minAsteroidSpeed, ::maxAsteroidSpeed); _asteroids.push_back(AsteroidPtr(new Asteroid(point, r, Vector(vx, vy)))); } void ModelHandler::addAsteroid(const AsteroidPtr& oldAsteroid, const Vector& newVelocity) { Point point = oldAsteroid->point(); point.move(newVelocity.normaVector() * oldAsteroid->collisionRadius()); _asteroids.push_back(AsteroidPtr(new Asteroid(point, oldAsteroid->collisionRadius() / ::smallAsteroidRadiusK, newVelocity))); } Point ModelHandler::randPoint(float r) const { float x = 0; float y = 0; switch (rand() % 4) { case 0: x = common::rangeRand(0 - r, _worldWidth + r); y = 0 - r; break; case 1: x = common::rangeRand(0 - r, _worldWidth + r); y = _worldHeight - r; break; case 2: x = 0 - r; y = common::rangeRand(0 - r, _worldHeight + r); break; case 3: x = _worldWidth + r; y = common::rangeRand(0 - r, _worldHeight + r); break; default: break; } return Point(x, y); } bool ModelHandler::withinBoundaries(const ObjectPtr& obj) const { return (obj->x() > (0 - _worldWidth * 0.1) && obj->x() < (_worldWidth * 1.1) && obj->y() > (0 - _worldHeight * 0.1) && obj->y() < (_worldHeight * 1.1)); } template<class T> void ModelHandler::checkObjects(std::list<T>* objects) { typename std::list<T>::iterator it = objects->begin(); while (it != objects->end()) { if (!this->withinBoundaries(*it)) { it = objects->erase(it++); } else { ++it; } } }
      
      









私の意芋では、リストされたテストタスクの䞭で、1぀だけが目立っおいたす。すべおの著者をむンタビュヌに招埅したした。ある候補者が他の候補者ずどのように異なっおいたか掚枬できたすか私にずっお、次のテストを芋るずほずんど明らかですが、友人は信じおいたせん。



All Articles