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ã€ã ããç®ç«ã£ãŠããŸãããã¹ãŠã®èè ãã€ã³ã¿ãã¥ãŒã«æåŸ ããŸãããããåè£è ãä»ã®åè£è ãšã©ã®ããã«ç°ãªã£ãŠãããæšæž¬ã§ããŸããïŒç§ã«ãšã£ãŠã次ã®ãã¹ããèŠããšã»ãšãã©æããã§ãããå人ã¯ä¿¡ããŠããŸããã