锘??xml version="1.0" encoding="utf-8" standalone="yes"?>
鍏徃 | 娓告垙钀ユ敹(涓囧厓) | 姣涘埄娑?涓囧厓) | 姣涘埄鐜?/td> |
鐩涘ぇ | 133600 | 80200 | 60% |
瀹岀編 | 54000 | 52600 | 86.60% |
鐣呮父 | 48250 | 28800 | 92% |
宸ㄤ漢 | 27300 | 23200 | 83.90% |
銆銆褰撶劧錛岃繖鏄崰鎹簡(jiǎn)鍥藉唴娓告垙琛屼笟鏀跺叆涓澶у崐鐨勫嚑瀹跺ぇ鍏徃錛屼笉榪囧氨綆楁槸灝忓叕鍙革紝鍙鑳芥垚鍔熻浜у搧涓婄嚎錛屽埄娑︾巼涔熸槸涓鏍風(fēng)殑銆?/font>
銆銆浠庝笂甯?jìng)鍏徃鐨勮储鎶ラ噷鍙互鐪嬪垘图屼竴鑸琈MORPG鐨勬瘡媧昏穬浠樿垂鐢ㄦ埛騫沖潎姣忔湀璐$尞鏀跺叆澶ц嚧鍦?0鍏冨乏鍙籌紝鏈変簺娓告垙浼?xì)楂樺緢澶氬Q屾湁浜涚◢浣庝竴浜涖備篃灝辨槸璇達(dá)紝鍙鏈?涓囨椿璺冧粯璐圭敤鎴鳳紝浠?5%鐨勫埄娑︾巼鏉ヨ綆楋紝鎵i櫎榪愯惀鎴愭湰鍚庣殑鏈堝埄娑﹀氨鑳借揪鍒?00涓囥?/font>
銆銆涓嶈繃錛岀洰鍓嶆父鎴忕殑綾誨瀷鍩烘湰涓婇兘鏄亾鍏鋒敹璐癸紝榪欐牱涓嶆槸姣忎釜鐜╂父鎴忕殑浜洪兘浼?xì)鎺忛挶銆傛寜鐓х晠娓歌儲(chǔ)鎶ヤ腑鎶湶鐨勪俊鎭紝闈炰粯璐圭敤鎴峰悜浠樿垂鐢ㄦ埛鐨勮漿鍖栨瘮渚嬩負(fù)18%錛屽叾浠栨父鎴忕殑榪欎釜姣斾緥搴旇涔熶笉浼?xì)鐩稿樊澶锛? * 18%錛屾湀鏀跺叆100涓囧氨闇瑕佹湁11涓囩殑媧昏穬鐢ㄦ埛銆?/font>
銆銆媧昏穬鐢ㄦ埛鐨勭粺璁℃柟娉曚竴鑸槸濡傛灉璇ヨ處鍙峰湪涓澶╁唴绱鍦ㄧ嚎鏃墮棿瓚呰繃2灝忔椂錛屽垰璇ョ帺瀹剁殑媧昏穬澶╂暟灝卞姞1錛屽鏋滃湪綰挎椂闂村湪鍗婂皬鏃跺埌2灝忔椂涔嬮棿錛屽垯媧昏穬澶╂暟鍔?.5錛屽湪綰挎椂闂村皬浜庡崐灝忔椂鐨勪笉綆楁椿璺冨ぉ鏁般傚綋鐒?dòng)灱寴q欐槸姣忔棩媧昏穬鐢ㄦ埛鏁扮殑緇熻鏂規(guī)硶銆?/font>
銆銆鎴戜滑浠?灝忔椂涓哄崟浣嶏紝鍙鍦ㄦ瘡涓椂闂村崟浣嶅唴鏈?1 / 12涓囩敤鎴峰湪綰匡紝鍒欏氨杈懼埌浜?0涓囨椿璺冪敤鎴楓備篃灝辨槸璇達(dá)紝騫沖潎鍦ㄧ嚎鍙涓嶅埌涓涓囦漢灝辮揪鍒頒簡(jiǎn)鏈堝叆100涓囩殑鐩爣銆?/font>
銆銆榪欎釜鏁板瓧鍩烘湰涓婃槸鍙俊鐨勩傚彲浠ュ啀鐪嬩竴鐪嬪畬緹庣殑璐㈡姤錛屽畬緹嶲4璐㈡姤鏄劇ず錛屽鉤鍧囧悓鏃跺湪綰匡紙ACU錛変負(fù)115.7涓囦漢錛岀綉娓告敹鍏ヤ負(fù)5.4浜垮厓錛屽鉤鍧囨瘡鏈堜負(fù)1.8浜匡紝榪欐牱姣?涓囧鉤鍧囧湪綰胯礎(chǔ)鐚殑鏀跺叆涓?18000 / 115.7 = 155涓囷紝榪欎釜鏁板瓧姣斿墠闈㈣綆楃殑涓嶅埌涓涓囧悓鏃跺湪綰胯礎(chǔ)鐚敹鍏?00涓囪繕瑕侀珮錛岃繖鏄洜涓哄畬緹庣殑ARPU鍊兼瘮鐣呮父楂橈紝涓烘瘡鏈?4鍏冦?/font>
銆銆鎵浠ワ紝姣忓綋璺熷埆浜轟粙緇嶆垜鏄仛娓告垙鏃訛紝鍒漢閮戒細(xì)鎰熷徆涓涓嬶紝娓告垙鍟婏紝鎸鴻禋閽辯殑錛屾垜涔熺瑧絎戯紝紜疄鏄尯璧氶挶鐨勩傚彲鏄紝濡傛璧氶挶鐨勪駭鍝侊紝寮鍙戜漢鍛樼殑鍥炴姤鍙堟湁澶氬皯鍛紵
銆銆鍐嶆潵鐪嬩竴涓嬩笂甯?jìng)鍏徃鐨勮储鎶ャ備緷鐒舵槸鐣呮父鐨勶紝“闈炵編鍥介氱敤浼?xì)璁″噯鍒欎骇鍝佸紑鍙戣垂鐢ㄤ負(fù)510涓囩編鍏冿紝鐜瘮澧為暱5%錛屽悓姣斿噺灝?2%銆傜幆姣斿闀夸富瑕佹槸鐢變簬鑱樼敤浜?jiǎn)鏇村鐨勬父鎴忕爺鍙戜汉鍛樿屽鑷村憳宸ヨ柂閰笌紱忓埄璐圭敤鐨勫鍔犮傚悓姣斿噺灝戜富瑕佹槸鐢變簬鍏徃綆$悊灞傚閲戞柟妗堢殑璋冩暣鈥斺斾箣鍓嶆巿浜堢殑鑲℃潈嬋鍔卞湪IPO鍚庝環(huán)鍊兼彁楂橈紝鍥犺屽叕鍙稿噺灝戝叾鐜伴噾濂栧姳銆?#8221;
銆銆浠庤繖鍙ヨ瘽涓婂彲浠ョ湅鍑烘潵錛岃繖涓爺鍙戣垂鐢ㄤ笉浠呬粎鍙槸寮鍙戜漢鍛樼殑宸ヨ祫錛岃繕鍖呮嫭浜?jiǎn)鐢变簬鍛樺伐鐨勭浉鍏臣洀鍒╁Q屼篃鍖呭惈浜?jiǎn)湄?fù)鍛樺伐鎵緙寸撼鐨勪繚闄╋紝鍏Н閲戠瓑璐圭敤銆傚彟澶栦篃榪樺寘鎷簡(jiǎn)濂栭噾錛屼互鍙?qiáng)鑲℃潈濂栧娹q瓑絳夈傚氨榪?20涓囷紝涔熷彧鍗犲埌浜?jiǎn)钀ユ敹鐨?%銆?/font>
銆銆鍦ㄥ叕鍙哥鐞嗗眰瑙h璐㈡姤鐨勬椂鍊欐湁鍙﹀涓鍙ヨ瘽錛?#8220;鎴鍒?009騫?2鏈?1鏃ワ紝鍏辨湁520浣嶅伐紼嬪笀銆傚湴闈㈡帹騫垮洟闃熶腑姝e紡鍛樺伐鏈?0浜猴紝鍙﹀榪樻湁涓存椂闆囧憳澶х害榪樻湁600浜恒?#8221;涓嶇煡閬撹繖510涓囩殑鐮斿彂璐圭敤鏄惁鍖呮嫭浜?jiǎn)涓婇潰鎻愬埌鐨勫湴闈㈡帹骞夸汉鍛樺拰鋸?fù)鏃墮泧鍛橈紝鏆傛椂璁や負(fù)鍏朵笉鍖呭惈鍚э紝鍋囧畾榪欎簺閽遍兘鑺卞湪浜?20浣嶅紑鍙戜漢鍛樿韓涓娿?/font>
銆銆鍙﹀涔熶笉鐭ラ亾榪?10涓囨槸鍚﹀寘鍚爺鍙戠浉宸殑鍔炲叕瀹ょ閲戠瓑璐圭敤錛岃繖涓及璁″簲璇ュ寘鍚惂銆?/font>
銆銆鍏徃涓哄憳宸ョ即綰崇殑淇濋櫓錛屽叕縐噾璺熸垜浠氦鐨勪竴鏍鳳紝浼?xì)鍗犲伐璧勭?0%澶氾紝鍔炲叕寮鏀笉澶уソ綆楋紝鍙﹀紱忓埄涔熶笉濂借錛屾瘮濡傝仛槨愶紝緇勭粐鍑哄幓鐜╃瓑錛岃繖浜涗笉濂界粺璁★紝榪樹笉鎺掗櫎鏈変竴浜涙棤娉曠粺璁″埌鐨勭伆鑹插唴瀹廣傜畝鍗曚竴鐐癸紝灝辯畻榪?10涓囨湁50%鏈鍚庝互money鐨勫艦寮忓彂鍒頒簡(jiǎn)寮鍙戜漢鍛樻墜涓婂惂錛屽寘鎷瘡鏈堝浐瀹氬伐璧勪笌濂栭噾銆傝繖鏍鳳紝騫沖潎姣忎漢姣忔湀鏄?600緹庡厓銆?/font>
銆銆鍥犱負(fù)澶ч儴鍒嗗叕鍙哥殑濂栭噾閮芥槸浠ュ鍙戜竴涓垨鑰呭嚑涓湀鐨勫伐璧勫艦寮忔潵鍙戞斁鐨勶紝鎹鐣呮父姣忓勾鐩稿綋浜庤兘鍙?6涓湀宸ヨ祫錛?600 * 12 / 16 = 1200緹庡厓銆傚鉤鍧?000鍏冪殑鏈堣柂錛屽彲鑳藉ぇ閮ㄥ垎浜轟細(xì)鎰熻鍒拌嚜宸卞茍娌℃湁榪欎箞澶氥傚綋鐒?dòng)灱屽洜湄?fù)鏈変竴灝忛儴鍒嗕漢鏈夎偂鏉冪殑濂栧姳錛屼粬浠殑鏀跺叆榪滆秴榪囦簡(jiǎn)8000 * 16錛屾墍璋?0%鐨勭簿鑻辮幏寰椾簡(jiǎn)80%鐨勫埄鐩?:) 鍏跺疄涔熸槸宸笉澶氱殑銆?/font>
銆銆濡傛楂樺埄娑︾巼鐨勬父鎴忎駭鍝侊紝鍒板ご鏉ョ敓浜ц呬滑鐨勫洖鎶ュ嵈涔熻繕鏄竴鏍楓傛墍浠ワ紝鍐嶅惉鍒頒漢璇存父鎴忓緢璧氶挶鏃訛紝鏈夊繀瑕佸ソ濂藉鍏惰В閲婁竴鐣?:) 娓告垙紜疄鏄禋閽憋紝鍙挶騫朵笉鍦ㄦ垜浠繖浜涗漢鐨勫彛琚嬮噷銆?/font>
銆銆鍐嶆潵鐪嬭繎鏈熺綉鏄撶殑澶ц瘽浜屽洟闃熼泦浣撶鑱岋紝澶ф槑榫欐潈寮鍙戝洟闃熼泦浣撹煩妲藉畬緹庯紝榪樻湁鍗庝箟鎴愰兘宸笉澶氭暣涓叕鍙歌煩鍒拌吘璁紝閲戝北鐨勫洟闃熷埌緗戞槗鍘誨紑鍙戝ぇ鍞愶紝鐩涘ぇ鐨勮嫳闆勫勾浠e洟闃熷嚭璧板緛閫旂瓑絳夛紝榪欎笉鑳借鏄憳宸ヤ笉澶熷繝璇氾紝瀹炲湪鏄埄鐩婂垎閰嶇殑涓嶅悎鐞嗐傛柊鐨勮佹澘鍙鑲鎷垮嚭涓涓敋鑷沖崐涓櫨鍒嗙偣錛屽垎鍒板紑鍙戜漢鍛樼殑鎵嬩腑灝辨槸娌夌敻鐢哥殑璇辨儜浜?jiǎn)銆?/font>
銆銆鑰屼笖榪樻湁鍙﹀涓鏉″緢閲嶈鐨勫洜绱狅紝娓告垙鐨勬垚鍔熶笌鍚﹀緢澶х▼搴︿笂鏉ヨ嚜浜庨偅鍑犱釜寮鍙戜漢鍛樸傜旱瑙傚浗鍐呮父鎴忓巶鍟嗭紝鍩烘湰涓婇兘鏄彧闈犵潃寮鍙戝洟闃熷仛鐨勪竴嬈炬父鎴忓氨鎾戣搗浜?jiǎn)鏁翠釜鍏徃锛岃鍏徃鎸q涗簩綰褲佷竴綰挎父鎴忓巶鍟嗚鍒楋紝鐢氳嚦闈犵潃榪欎竴嬈炬父鎴忓幓璧氱編鍥借偂姘戠殑閽便傚綋鐒惰繖縐嶆垚鍔熶篃鏈夊緢澶х殑鍋剁劧鎬с?/font>
銆銆灝卞儚涓涓鍦ㄥ垱涓氳礬涓婄殑鏈嬪弸鎵璇達(dá)紝娓告垙鏄兘璁╀綘榪呴熶粠鏉ㄧ櫧鍔沖彉鎴愰粍涓栦粊鐨勬渶濂介夋嫨銆傚綋鍓嶏紝鍓嶆彁鏄綘涓嶆槸鍦ㄤ負(fù)鍒漢鎵撳伐銆?/font>
綆鍗曟潵璇達(dá)紝褰撴垜浠湪DX涓嬌鐢⊿hader鐨勬椂鍊欙紝鎴戜滑浼?xì)鋴社敤涓涓父閲忚〃鏉ヨ緗悇縐嶅弬鏁板鹼紝鍏朵腑鏈閲嶈鐨勫綋灞炰笘鐣屽彉鎹㈢煩闃典簡(jiǎn)銆傝繖鍦ㄧ敤DX鍐欐父鎴忕▼搴忔椂娌℃湁闂錛屼絾鏄湪鍏朵粬涓浜汼hader緙栬緫宸ュ叿涓棶棰樺氨鍑烘潵浜?jiǎn)锛屽浣曡畡〗畼q欎簺鍙橀噺錛熷綋鐒?dòng)灱屾瘡绉嶅伐鍏峰彲浠ユ彁渚涜嚜宸辩殑鏂规硶锛屾瘮濡傜幇鍦∕ax錛孧aya閮芥彁渚涗簡(jiǎn)鑷繁鐨勬柟娉曞湪Shader涓粦瀹氫笘鐣屽彉鎹㈢煩闃碉紝浣嗛棶棰樻槸姣忕宸ュ叿鎻愪緵鐨勬柟娉曢兘涓嶄竴鏍鳳紝榪欐牱鍐欏嚭鏉ョ殑Shader鏂囦歡灝遍夯鐑?chǔ)浜?jiǎn)銆傘傘?/font>
浜庢槸錛孧S榪欐椂绔欏嚭鏉ワ紝鎻愬嚭浜?jiǎn)涓涓爣鍑嗭細(xì)DXSAS銆傝繖鏄洰鍓嶈繕鑳芥壘鍒扮殑DXSAS鐨勪竴孌墊弿榪幫細(xì)
Standard annotations and semantics (DXSAS) provide a method of using shaders in a standard way that enables shaders to be used with tools, applications, and game engines. DXSAS defines a set of semantics and annotations that are attached to host application values and effect parameters for the purpose of sharing effects.
鍦板潃錛?a target="_blank">http://msdn.microsoft.com/en-us/library/bb173004(VS.85).aspx
鏈夋爣鍑嗘槸濂戒簨錛岃屼笖鎴戜滑涔熻兘鐪嬪埌錛屽ぇ閲忕殑Shader宸ュ叿鍙?qiáng)娓告垙寮曟搸涔熶娇鐢ㄥ堫C簡(jiǎn)SAS錛屾瘮濡侳XComposer錛屾瘮濡俁enderMonkey錛屾瘮濡侰ryEngine銆傘傘傚綋鐒?dòng)灱屽悇宸ュ叿鍙兘瀵筍AS閮戒細(xì)鏈夎嚜宸辯殑鎵╁睍錛屼絾澶ч儴鍒嗚繕閮芥槸鎸夌収鏍囧噯鏉ョ殑銆?/font>
鍙槸錛屼負(fù)浠涔圡S紿佺劧灝卞皢榪欐爣鍑嗙Щ闄や簡(jiǎn)銆傘傘傝屼笖錛屾病鏈変換浣曠悊鐢辯殑銆傘傘備互鑷充簬FXComposer1.8鐗堟湰浠ュ墠鐨勬枃妗d腑闄勫甫鐨凷AS鎻忚堪绔犺妭涔熻窡鐫涓騫跺垹闄わ紝鎴戝湪FXComposer2.5涓婃壘閬嶄簡(jiǎn)鏁翠釜鎵嬪唽涔熸病鎵懼埌榪欎釜 WorldViewProjection 浠庡摢閲屾潵錛岀炕閬嶄簡(jiǎn)MSDN涓奌LSL鏈夊叧鐨勫唴瀹逛篃娌℃湁鐪嬪埌榪欏嚑涓叧閿瓧銆傛棤濂圙oogle涔嬶紝鍘熸潵鍙楁鍥版儜鐨勪漢榪樼湡涓嶅皯銆?/font>
濂藉湪錛孨vidia鎺ㄥ嚭浜?jiǎn)涓浠芥柊鐨勬枃妗o紝鎻忚堪SAS鐨勪嬌鐢ㄦ柟娉曪紝鎴栬Nvidia涔熷緢鍥版儜錛?#8220;which came from DirectX 9, and are sometimes hard to find”銆傘傘備絾鏄柊鐨凢XComposer鎵嬪唽涓榪欎簺鍙瓧鏈彁鍗存槸鎴戜粛鐒跺緢鍥版儜鐨勶紝瀵逛簬鍍忔垜榪欐牱鐨勪竴涓柊鎵嬫潵璇達(dá)紝瀹屽叏涓嶅彲鑳界煡閬撹繖浜涗笢瑗挎槸濡備綍鏉ョ殑錛屽浣曞幓鎵捐繖浜汼emantics鐨勫畾涔夈?/font>
Nvidia緗戠珯涓婄殑SAS璇存槑鏂囨。錛?a target="_blank">http://developer.nvidia.com/object/using_sas.html
浠ュ強(qiáng)FXComposor瀹樻柟璁哄潧涓婄疆欏剁殑鍏充簬SAS鏂囨。鐨勮鏄庤創(chuàng)錛屽綋鏃舵垜绔熺劧娌$湅鍒拌繖涓疆欏惰創(chuàng) :( http://developer.nvidia.com/forums/index.php?showtopic=1358
鍏朵粬浜烘彁鍑虹殑涓浜涚浉鍏崇枒闂細(xì)
http://developer.nvidia.com/forums/index.php?showtopic=750
http://developer.nvidia.com/forums/index.php?showtopic=31
http://developer.nvidia.com/forums/index.php?showtopic=1061
http://developer.nvidia.com/forums/index.php?showtopic=1347
http://developer.nvidia.com/forums/index.php?showtopic=1394
鐣欎笅榪欎簺璁板綍錛屼篃璁告湁璺熸垜涓鏍風(fēng)殑鍒濆摜浠紝灝戠偣鍥版儜 :)
榪欑瘒鏂囩珷鏈鏃╂槸鍑虹幇鍦╢lipcode鐨勮鍧涗笂錛?a target="_blank">鍦板潃鍦ㄨ繖閲?/a>錛屽悗鏉ユ湁浜烘暣鐞嗕簡(jiǎn)涓涓嬶紝騫舵坊鍔犱簡(jiǎn)鏇村鐨勬弿榪幫紝涔熷氨鏄笅闈㈢殑鍐呭銆?a target="_blank">鍘熻創(chuàng)鍦板潃鍦ㄨ繖閲?/a>銆?/font>
鏂囩珷涓叧浜庣綉緇滅殑鎻忚堪鏄寚灞鍩熺綉鐜涓嬬殑鎯呭喌錛屽彟澶栬繖涓富寰幆涔熸病鏈夎冭檻鍒皐indows鐜涓嬩笌Windows Message Loop鐨勭粨鍚堬紝濡傛灉鏄簲鐢ㄥ湪windows鐜涓嬶紝鍙互鍐?a target="_blank">鍙傝冧笅榪欓噷錛屾妸涓よ呯粨鍚堝熀鏈笂灝卞樊涓嶅浜?jiǎn)銆?/font>
緲昏瘧騫舵湭涓ユ牸閬電収鍘熸枃錛屼負(fù)浜?jiǎn)璇昏典h潵嫻佺晠錛屽緢澶氬彞瀛愰兘鏄寜鐓ф垜涓漢鐨勭悊瑙f潵鎻忚堪銆?/font>
This article is about a way of structuring a game's main loop. It includes techniques for handling view drawing with interpolation for smooth animation matched to the frame-rate with fixed-step game logic updating for deterministic game logic. A lot of this is still pretty much a work-in-progress as I muddle my way through, learning better ways of doing things or new tricks to add to my bag, so please bear with me.
榪欑瘒鏂囩珷鎻忚堪鐨勬槸濡備綍緇勭粐娓告垙涓誨驚鐜殑涓縐嶆柟娉曘傚唴瀹瑰寘鎷簡(jiǎn)濡備綍澶勭悊騫蟲粦鐨勫姩鐢葷粯鍒訛紝騫朵笖鑳藉鏍規(guī)嵁褰撳墠甯х巼鍋氭紜殑鍔ㄧ敾鎻掑鹼紝鍙﹀榪樿淇濊瘉鍥哄畾鐨勬父鎴忛昏緫鏇存柊甯х巼錛屼互紜繚娓告垙閫昏緫鐨勮綆楃粨鏋滄槸紜畾鐨勩?/font>
榪欎簺鍐呭鐨勫疄鐜版湁寰堝閮借繕鍦ㄨ繘琛屼腑錛屾垜涔熷湪涓嶆柇鍦板涔?fàn)鏇村ソ鐨勬栆?guī)硶錛屼互鍙?qiáng)灏嗕竴浜涙柊鐨勬妧宸ф坊鍔犺繘鏉ワ紝鎵浠ワ紝甯屾湜鑳藉緇欐垜涓浜涜愬績(jī)涓庡瀹廣?/font>
The heart of a game, any game, is the game loop. This is where the action takes place, where the guns fire and the fireball spells fly. In some games, the concept of the game loop may be diffused among different components or game states, which implement their own version of the game loop to be exectued at the proper time, but the idea is still there.
浠諱綍娓告垙鐨勬牳蹇?jī)閮芥槸娓告垙涓诲惊鐜傛父鎴忕殑鍔ㄤ綔鎵ц銆佸瓙寮圭殑灝勫嚮浠ュ強(qiáng)鐏悆欖旀硶鐨勯琛岀瓑絳夐兘鏄湪榪欓噷瀹炵幇銆?/font>
鍦ㄤ竴浜涙父鎴忎腑錛屼綘鍙兘浼?xì)鎵句笉鍒颁竴涓敮涓鐨勬父鎴忎富寰幆錛屽彇鑰屼唬涔嬬殑鏄紝浣犱細(xì)鍦ㄤ竴浜涚粍浠跺強(qiáng)鐘舵佹満涓壘鍒板悇涓笉鍚岀増鏈殑涓誨驚鐜?/font>
鍏跺疄榪欎釜鍘熺悊涔熸槸涓鏍風(fēng)殑錛屽彧涓嶈繃鏄妸鍘熸潵鐨勪竴涓富寰幆鎷嗗垎鎴愪簡(jiǎn)澶氫釜錛岃繖鏍峰彲浠ユ帶鍒舵父鎴忓湪涓嶅悓鐨勬椂闂村強(qiáng)鐘舵佷笅鎵ц涓嶅悓鐨勫驚鐜繃紼嬨?/font>
The game loop is just that: a loop. It is a repeating sequence of steps or actions which are executed in a timely and (hopefully) efficient manner, parceling out CPU time to all of the myriad tasks the game engine is required to perform: logic, physics, animation, rendering, handling of input. It must be constructed in a deterministic, predictable fashion so as to give expected behavior on a wide array of hardware configurations. Classic failures in this regard include old pre-Pentium DOS-based games that were synchronized to run well on old hardware, but which did not have the controls in place to control the speed on newer hardware, and consequently ran so rapidly that they became unplayable on new hardware. With such a broad diversity of hardware as now exists, there must be tighter controls on the game loop to keep it running at a consistent speed, while still taking advantage of more powerful hardware to render smoother animation at higher framerates.
綆鍗曟潵璇達(dá)紝娓告垙涓誨驚鐜氨鏄竴涓驚鐜繃紼嬨?/font>
鍦ㄨ繖涓笉鏂噸澶嶆墽琛岀殑榪囩▼涓紝鎴戜滑闇瑕佹妸CPU鏃墮棿鎸夌収涓瀹氱殑瑙勫垯鍒嗛厤鍒頒笉鍚岀殑浠誨姟涓婏紝榪欎簺浠誨姟鍖呮嫭錛氶昏緫銆佺墿鐞嗐佸姩鐢匯佹覆鏌撱佽緭鍏ュ鐞嗙瓑絳夈?/font>
娓告垙鐨勪富寰幆蹇呴』淇濊瘉鏄互涓縐嶇‘瀹氱殑銆佸彲棰勬祴鐨勬柟寮忔潵鎵ц錛岃繖鏍鋒墠鑳藉湪澶ч噺鐨勪笉鍚岀‖浠墮厤緗幆澧冧笅閮藉緱鍒版垜浠墍鏈熸湜鐨勭浉鍚岃涓恒?/font>
浠ュ墠鐨凞OS娓告垙鏇劇粡鍑虹幇榪囪繖鏍風(fēng)殑闂錛屽畠浠湪鏃х殑紜歡涓婅繍琛岀殑寰堝ソ錛屼絾鏄斁鍒版柊鐨勭‖浠朵笂浠ュ悗灝卞け鍘繪帶鍒朵簡(jiǎn)錛屾父鎴忕殑榪愯閫熷害鍙樼殑濡傛涔嬪揩錛屼互鑷充簬鏍規(guī)湰鏃犳硶鍘葷帺瀹冦?/font>
鐜板湪甯?jìng)鍦轰笂瀛樺湪杩欎箞澶氱殑纭欢绉嵕c伙紝鎵浠ュ氨蹇呴』瑕佺揣绱у湴鎺у埗浣忔父鎴忕殑涓誨驚鐜紝淇濊瘉浠栦滑浠ヤ竴涓浐瀹氱殑閫熷害榪愯錛屼絾鍚屾椂鍙堣兘鑾峰緱榪欎簺寮哄ぇ鐨勭‖浠舵墍甯︽潵鐨勫ソ澶勶細(xì)浠ュ敖鍙兘楂樼殑甯х巼鏉ユ覆鏌撳嚭鏇村鉤婊戠殑鍔ㄧ敾銆?/font>
Older games frequently tied the rendering of the view very closely to the game loop, drawing the view exactly once per logic update and waiting for a signal from the display system indicating a vertical retrace period, when the electron gun in the CRT monitor was resetting after drawing the screen. This synchronized loops to a predictable rate based on the refresh rate of the monitor, but with the advent of customizable refresh settings this leads again to unpredictable loop behavior. Retrace synchronization is still useful, especially to avoid visual artifacts when rendering the view, but is less useful as a means for synchronizing the game logic updating, which may require finer control.
浠ュ墠鐨勬父鎴忕粡甯稿皢娓叉煋榪囩▼涓庢父鎴忓驚鐜揣瀵嗗湴緇戝湪涓璧鳳紝棣栧厛鎵ц涓嬈¢昏緫鏇存柊錛岀劧鍚庣粯鍒剁敾闈紝鎺ョ潃絳夊緟鏄劇ず緋葷粺瑙﹀彂涓涓瀭鐩村悓姝ヤ俊鍙鳳紝涔嬪悗灝辨槸涓嬩竴杞驚鐜懆鏈燂細(xì)閫昏緫鏇存柊銆佹覆鏌撱佺瓑寰?#8230;…鍛ㄨ屽濮嬨?/font>
榪欑鍚屾鐨勫驚鐜柟娉曞湪鏄劇ず鍣ㄧ殑鍒鋒柊鐜囧彲棰勬祴鐨勬儏鍐典笅鏄湁鏁堢殑錛屼絾鏄綋鍙互鑷畾涔夊埛鏂扮巼浠ュ悗錛岃繖縐嶈涓哄張鍙樺緱涓嶅彲棰勬祴浜?jiǎn)銆?/font>
鍨傜洿鍚屾浠嶇劧鏄湁鐢ㄧ殑錛屽挨鍏舵槸鍦ㄩ伩鍏嶇敾闈㈢殑娓叉煋鍑虹幇鎾曡鐨勬儏鍐典笅錛屼絾鏄敤鏉ュ悓姝ユ父鎴忕殑閫昏緫鏇存柊灝辨病澶氬ぇ鐢ㄤ簡(jiǎn)錛岃繖鏃跺彲鑳介渶瑕佹洿濂界殑鎺у埗鏂規(guī)硶銆?/font>
The trick, then, is to separate game logic from rendering, and perform them in two separate sub-systems only marginally tied to each other. The game logic updates at it's own pace, and the rendering code draws the screen as fast as it possibly with the most accurate, up-to-date data the logic component can provide.
榪欑鏂規(guī)硶灝辨槸灝嗘父鎴忛昏緫鏇存柊涓庡睆騫曟覆鏌撹繃紼嬪垎紱誨紑錛屽皢浠栦滑鏀懼埌涓や釜鍒嗙鐨勫瓙緋葷粺涓幓澶勭悊錛屽彧鏄湪闇瑕佺殑鏃跺欐墠涓庡彟涓涓墦浜ら亾銆?/font>
娓告垙鐨勯昏緫鏇存柊涓ユ牸鎸夌収璁″垝鏉ユ墽琛岋紝浣嗘槸灞忓箷娓叉煋鍒欎互瀹冩墍鑳借揪鍒扮殑鏈澶ч熺巼鏉ヨ繘琛屻?/font>
The system I am accustomed to using is based on a Tip of the Day ( http://www.flipcode.com/cgi-bin/msg.cgi?showThread=Tip-MainLoopTimeSteps&forum=totd&id=-1 ) posted to http://www.flipcode.com by Javier Arevalo. It implements a loop wherein the game logic is set to update a fixed number of times per second, while the rendering code is allowed to draw as rapidly as possible, using interpolation to smooth the transition from one visual frame to the next.
榪欓噷鎻忚堪鐨勬柟娉曞熀浜嶫avier Arevalo鍙戣〃鍦?/font>flipcode Tip of the Day 涓婄殑浠g爜鏉ュ疄鐜般?/font>
鍦ㄨ繖涓父鎴忎富寰幆閲岋紝娓告垙閫昏緫鏇存柊琚緗負(fù)姣忕鎵ц鍥哄畾嬈℃暟錛屼絾鍚屾椂娓叉煋浠g爜琚厑璁告墽琛屽敖鍙兘澶氭錛屽茍涓旇繕浣跨敤浜?jiǎn)鎻掑兼潵浣垮緱涓や釜娓叉煋甯т箣闂寸殑鍔ㄧ敾鍙樺寲灝藉彲鑳界殑騫蟲粦銆?/font>
Briefly, here is the code. I will then attempt in my own crude fashion to explain the workings, though I suggest you check out the original tip at the above link to read Javier's explanation, as well as the forum posts accompanying it which offer up insights and suggestions on how the performance of the loop may be improved.
闂茶瘽灝戣錛屼笅闈㈤鍏堟槸浠g爜錛岀劧鍚庢垜浼?xì)绠鍗曠殑鎸夋垜鐨勬柟寮忔弿榪頒竴涓嬩唬鐮佺殑宸ヤ綔鍘熺悊錛屽悓鏃舵垜寤鴻浣犻槄璇諱竴涓嬩笂闈㈤摼鎺ュ湴鍧鎵緇欏嚭鐨勫唴瀹癸紝鍏朵腑鏈塉avier鐨勮В閲婏紝騫朵笖璁哄潧涓婄殑鍥炶創(chuàng)涔熸湁涓浜涗笉閿欑殑鍐呭錛屽寘鎷埆浜虹殑璇勮鍜屼竴浜涘叧浜庡浣曟彁楂樻晥鐜囩殑寤鴻銆?/font>
(娉細(xì)flipcode璁哄潧鏃╁氨宸茬粡鍏抽棴錛屼笂闈㈢殑閾炬帴鍦板潃宸茬粡澶辨晥錛宖lipcode涓婂彧淇濈暀鏈変竴浜涗紭縐鍐呭鐨?a >archives錛?a target="_blank">鍦ㄨ繖閲岃兘鎵懼埌榪欑瘒鍘熸枃錛屽叾涓寘鎷琂avier鐨勮В閲?
time0 = getTickCount(); do { time1 = getTickCount(); frameTime = 0; int numLoops = 0; while ((time1 - time0) > TICK_TIME && numLoops < MAX_LOOPS) { GameTickRun(); time0 += TICK_TIME; frameTime += TICK_TIME; numLoops++; } IndependentTickRun(frameTime); // If playing solo and game logic takes way too long, discard pending time. if (!bNetworkGame && (time1 - time0) > TICK_TIME) time0 = time1 - TICK_TIME; if (canRender) { // Account for numLoops overflow causing percent > 1. float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME); GameDrawWithInterpolation(percentWithinTick); } } while (!bGameDone);
Structurally, the loop is very simple. The above snippet of code can encapsulate the entire workings of your game.
浠庣粨鏋勪笂鏉ヨ錛岃繖涓富寰幆闈炲父綆鍗曘備笂闈㈢殑浠g爜鐗囨鍩烘湰涓婅兘澶熷泭鎷綘鐨勬父鎴忕殑鏁翠釜宸ヤ綔榪囩▼銆?/font>
First of all, the main loop portion is embodied as the do{} while(!bGameDone); block. This causes the loop to run endlessly, until some game condition indicates that it is finished and it is time to exit the program, at which point the loop ends and the game can be properly shut down. Each time through the loop, we perform game logic updates, input updating and handling, and rendering. Now, for a breakdown of the sections of the loop.
棣栧厛錛屼富寰幆鐨勬墽琛岃繃紼嬭鍖呭湪do…while寰幆鍧椾腑錛岃繖浣垮緱娓告垙涓誨驚鐜皢姘鎬笉緇撴潫鍦拌繍琛岋紝鐩村埌娓告垙鏄庣‘鍦拌鍛婄煡闇瑕佺粨鏉熷茍涓旈鍑虹▼搴忔椂涓烘銆?/font>
鍦ㄦ瘡嬈¤繘鍏ュ驚鐜殑鏃跺欙紝鎴戜滑浼?xì)鎵ц娓告垙閫昏緫鐨勬洿鏂幫紝杈撳叆鏇存柊涓庡鐞嗭紝榪樻湁娓叉煋銆傛帴涓嬫潵錛屾妸寰幆鍒嗕負(fù)鍑犱釜鐗囨鏉ユ弿榪般?/font>
time1 = getTickCount(); frameTime = 0; int numLoops = 0; while ((time1 - time0) > TICK_TIME && numLoops < MAX_LOOPS) { GameTickRun(); time0 += TICK_TIME; frameTime += TICK_TIME; numLoops++; }
This portion is the game logic update sequence that forces the game logic (physics updates, object motion, animation cycling, etc...) to update a set number of times per second. This rate is controlled by the TICK_TIME constant, which specifies the number of milliseconds the logic update is supposed to represent in real time. It probably won't take that long to perform, in which case the update won't be performed again until enough time has passed. For example, with TICK_TIME=40, each logic represents 40 milliseconds, thus forcing the code to update game objects at a rate of 25 times per second.
榪欓儴鍒嗕唬鐮佸鐞嗘父鎴忛昏緫鐨勬洿鏂幫紝騫朵笖寮哄埗瑕佹眰娓告垙閫昏緫姣忕鎵ц鍥哄畾嬈℃暟銆?/font>
娓告垙鐨勯昏緫澶勭悊鍖呮嫭鐗╃悊鏇存柊銆佸璞¤涓恒佸姩鐢誨驚鐜瓑絳夈傛洿鏂伴熺巼閫氳繃TICK_TIME甯擱噺鎸囧畾錛屽叾鍚箟涓轟袱嬈¢昏緫鏇存柊鐨勬椂闂撮棿闅旓紝鍗崇粡榪嘥ICK_TIME鍚庡簲璇ュ啀嬈¤繘琛屾洿鏂幫紝鑰屼笉鏄涓嬈¢昏緫鏇存柊瑕佹寔緇繖涔堥暱鏃墮棿銆?/font>
渚嬪錛孴ICK_TIME = 40錛屽叾鍚箟涓烘瘡嬈℃父鎴忛昏緫鏇存柊鍚庤〃鐜?0姣錛岃繖灝嗕嬌寰楁瘡縐掓父鎴忓璞′細(xì)琚洿鏂?5嬈°?/font>
The logic is encapsulated in it's own while loop. It is possible during a given frame that game logic will take too long. If a logic update cycle goes overtime, we can delay rendering for a bit to give ourselves a little extra time to catch up. The while loop will continue to process game logic updates until we are no longer overtime, at which point we can go ahead and continue on with drawing the view. This is good for handling the occasional burp in logic updating, smoothing out the updates and keeping them consistent and deterministic; but in the case that the logic repeatedly goes overtime, it is possible to accumulate more time-debt than the loop can handle. Thus, the MAX_LOOPS constant is in place to dictate a maximum number of times the loop can repeat to try to catch up. It will force the loop to dump out periodically to handle other tasks such as input handling and rendering. A loop that is constantly running at the MAX_LOOPS limit runs like hell and is about as responsive as a tractor with four flat tires, but at least it keeps the loop operating, allowing the user to pass input to terminate the program. Without the MAX_LOOPS failsafe, it would be entirely possible for a slow computer to lock up with no way to exit as it chokes on the loop, trying to catch up but getting further and further behind.
閫昏緫鏇存柊鐨勪唬鐮佽鍖呭湪浠栬嚜宸辯殑while寰幆涓?/font>
鍙兘鍦ㄦ煇涓甯ч噷娓告垙閫昏緫鐨勫鐞嗘椂闂翠細(xì)闈炲父闀匡紝鐢氳嚦浼?xì)瓒呮椨灱寴q欐椂鎴戜滑鍙互紼嶇◢鏆傚仠涓涓嬪睆騫曠殑娓叉煋錛屼嬌寰楁父鎴忛昏緫鏇存柊鑳藉鍦ㄨ繖孌墊椂闂撮噷璧朵笂鏉ャ?/font>
榪欎釜while寰幆灝辨槸鐢ㄦ潵璁╂父鎴忛昏緫緇х畫鏇存柊錛岀洿鍒版垜浠笉鍐嶈秴鏃訛紝涔嬪悗鎴戜滑緇х畫娓告垙鐨勪富寰幆騫朵笖榪涜灞忓箷娓叉煋銆?/font>榪欏彲浠ュ緢濂藉湴澶勭悊紿佸彂鐨勯昏緫鏇存柊瓚呮椂錛屼嬌寰楁垜浠殑鏇存柊鏇村姞騫蟲粦錛屽茍淇濊瘉閫昏緫鐨勪竴鑷存у拰鍙嫻嬫с?/font>
浣嗘槸濡傛灉娓告垙閫昏緫澶勭悊鎸佺畫鍦拌秴鏃訛紝鐢氳嚦浣垮緱鎴戜滑鐨勪富寰幆鏃犳硶澶勭悊榪囨潵錛岃繖鏃禡AX_LOOPS灝嗕細(xì)璧峰埌浣滅敤錛屼粬灝嗕嬌娓告垙鎺у埗鏉冧粠閫昏緫鏇存柊涓煩鍑烘潵錛屽幓鎵ц濡傝緭鍏ュ鐞嗗拰灞忓箷娓叉煋絳夊叾浠栦換鍔°?/font>
MAX_LOOP甯擱噺闄愬埗浜?jiǎn)杩欓噷鐨剋hile寰幆鐢ㄦ潵榪借刀閫昏緫澶勭悊鏃墮棿鏃舵渶澶氳兘閲嶅鐨勬鏁般?/font>榪欐牱褰撴父鎴忕湡鐨勫嚭鐜板畬鍏ㄦ棤娉曞鐞嗗畬閫昏緫鏇存柊鐨勬椂鍊欙紝鐢ㄦ埛涔熸湁鏈轟細(xì)緇撴潫紼嬪簭銆?/font>
濡傛灉娌℃湁MAX_LOOP鐨勬嫻嬶紝寰堟湁鍙兘浼?xì)鍑虹庮C竴鍙板緢鎱㈢殑鐢?shù)鑴戣瘯鍥緲q戒笂閫昏緫澶勭悊鏃墮棿錛屼絾鏄嵈瓚婅拷瓚婅繙錛岃屽張娌℃湁鏈轟細(xì)閫鍑?guó)櫩欎釜杩嚱E嬶紝鏈鍚庨櫡鍦ㄤ簡(jiǎn)榪欎釜姝誨驚鐜腑……
IndependentTickRun(frameTime);
This section is where input is gathered, events are pumped from the event queue, interface elements such as life bars are updated, and so forth. Javier allows for passing how much time the logic updates took, which can be used for updating on-screen clock or timer displays and the like if necessary. I've never found occasion to use it, but I regularly pass it anyway on the off-chance I'll need it someday. frameTime basically gives the amount of time that was spent in the logic loop performing updates.
榪欓儴鍒嗕唬鐮佺敤鏉ュ鐞嗙敤鎴瘋緭鍏ョ殑鎹曡幏錛屼簨浠朵細(xì)浠庝簨浠墮槦鍒椾腑琚彇鍑烘潵澶勭悊錛岀晫闈㈠厓绱狅紝濡傝鏉$瓑錛屼細(xì)鍦ㄨ繖閲岃鏇存柊錛岃繕鏈夊叾浠栫被浼肩殑澶勭悊銆?/font>
Javier鍦ㄨ繖閲岀暀浜?jiǎn)涓涓猣rameTime鍙傛暟錛岀敤鏉ユ寚鏄庨昏緫鏇存柊鎵鑺辯殑鏃墮棿銆傚彲浠ョ敤榪欎釜鍊兼潵鏇存柊娓告垙灞忓箷涓婄殑鏃墮挓鎴栧畾鏃跺櫒絳夌被浼間俊鎭?/font>
铏界劧鎴戠洰鍓嶈繕娌℃湁鎵懼埌鏈轟細(xì)鐢ㄥ畠錛屼絾鎴戣繕鏄範(fàn)鎯у湴鎶婂畠甯︿笂浜?jiǎn)锛屼篃璁告煇澶╂垜浼?xì)闇瑕併?/font>
In order for the game loop to run predictably, you should not modify anything in this step that will affect the game logic. This portion of the loop does not run at a predictable rate, so changes made here can throw off the timing. Only things that are not time-critical should be updated here.
涓轟簡(jiǎn)浣挎父鎴忓驚鐜殑榪愯鏄彲棰勬祴鐨勶紝浣犱笉搴旇鍦ㄨ繖閲屼慨鏀逛換浣曞彲鑳藉獎(jiǎng)鍝嶆父鎴忛昏緫鐨勪笢瑗匡紝鍥犱負(fù)姝ら儴鍒嗗湪娓告垙涓誨驚鐜腑鐨勬墽琛屾鏁版槸涓嶅彲棰勬祴鐨勶紝鎵浠ュ湪榪欓噷鍙兘鍋氫竴浜涙椂闂存棤鍏崇殑鏇存柊銆?/font>
// If playing solo and game logic takes way too long, discard pending time. if (!bNetworkGame && (time1 - time0) > TICK_TIME) time0 = time1 - TICK_TIME;
This is where we can shave off our time debt if MAX_LOOPS causes us to dump out of the logic loop, and get things square again--as long as we are not running in a network game. If it is a single-player game, the occasional error in the timing of the game is not that big of a deal, so sometimes it might be simpler when the game logic overruns it's alloted time to just discard the extra time debt and start fresh. Technically, this makes the game "fall behind" where it should be in real time, but in a single player game this has no real effect. In a network game, however, all computers must be kept in synchronization, so we can't just cavalierly discard the pending time. Instead, it sticks around until the next time we enter the logic update loop, where the loop has to repeat itself that many more times to try to catch up. If the time burp is an isolated instance, this is no big deal, as with one or two cycle overruns the loop can catch up. But, again, if the logic is consistently running overtime, the performance of the game will be poor and will lag farther and farther behind. In a networked game, you might want to check for repeated bad performance and logic loop overruns here, to pinpoint slow computers that may be bogging the game down and possibly kick them from the game.
褰撶敱浜庢弧瓚充簡(jiǎn)MAX_LOOP鏉′歡鑰岃煩鍑洪昏緫寰幆鏃訛紝鍙互鍦ㄨ繖閲屽噺鎺夊鍔犵殑涓奣ICK_TIME鏃墮棿錛屽茍涓斿彧鍦ㄥ崟鏈烘父鎴忔椂鎵嶈繖鏍峰仛銆?/font>
濡傛灉鏄湪鍗曟満娓告垙涓紝鍋跺皵鍑虹幇鏃墮棿涓婄殑閿欒鏄笉浼?xì)鏈変粈涔堝ぇ闂鐨勶紝鎵浠ュ綋娓告垙閫昏緫鎵ц瓚呮椂鍚庝篃娌℃湁澶氬ぇ鍏崇郴錛屾垜浠畝鍗曠殑鎶婂鑺辯殑鏃墮棿鍑忔帀錛岀劧鍚庨噸鏂板紑濮嬨備粠鎶鏈笂鏉ヨ錛岃繖浼?xì)鋴慑緱娓告垙鏈変竴鐐圭偣鏃墮棿涓婄殑钀藉悗錛屼絾鍦ㄥ崟鏈烘父鎴忛噷榪欎笉浼?xì)鏈変粈涔堝疄闄呯殑褰卞搷銆?/font>
浣嗘槸鍦ㄧ綉緇滄父鎴忎腑榪欐牱鍋氬嵈涓嶈錛屾墍鏈夎繛緗戠殑鐢?shù)鑴戦兘蹇厵寤瑕佷繚鎸佹棄櫁翠笂鐨勫悓姝ャ?/font>
鍦ㄧ綉緇滄父鎴忎腑錛屽鏋滄煇鍙扮數(shù)鑴戣惤鍚庝簡(jiǎn)錛屼粬搴旇淇濇寔鍏惰惤鍚庣殑鐘舵侊紝鐒跺悗鍦ㄤ笅涓嬈¤繘鍏ラ昏緫鏇存柊寰幆鏃訛紝鑷繁璁╄嚜宸卞閲嶅鍑犳錛屼互渚胯拷璧朵笂鏉ャ?/font>
濡傛灉鏃墮棿寤惰繜鍙槸涓绔嬩簨浠訛紝榪欏皢涓嶄細(xì)鏈夊澶ч棶棰橈紝緇忚繃涓涓ゆ瓚呴熷氨浼?xì)杩借刀涓婃潵锛屼絾鏄鏋滄父鎴忛昏緫鏇存柊鎬繪槸瓚呮椂錛屾父鎴忕殑琛ㄧ幇灝嗕細(xì)闈炲父緋熺硶錛屽茍涓旀渶緇堝皢浼?xì)瓒婃潵瓒婃粸鍚庛?/font>
鍦ㄧ綉緇滄父鎴忎腑錛屼綘鍙兘闇瑕佹鏌ュ嚭榪欎簺鎸佺畫瓚呮椂錛岃〃鐜版繪槸寰堝樊鐨勭數(shù)鑴戯紝騫跺皢榪欎簺鍙兘鎷栨參鏁翠釜娓告垙鐜鐨勭數(shù)鑴戣涪鍑哄幓銆?/font>
// Account for numLoops overflow causing percent > 1. float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME); GameDrawWithInterpolation(percentWithinTick);
This is where the real magic happens, in my opinion. This section performs the rendering of the view. In the case of a loop where TICK_TIME=40, the logic is updating at 25 FPS. However, most video cards today are capable of far greater framerates, so it makes no sense to cripple the visual framerate by locking it to 25 FPS. Instead, we can structure our code so that we can smoothly interpolate from one logic state to the next. percentWithinTick is calculated as a floating point value in the range of [0,1], representing how far conceptually we are into the next game logic tick.
鍦ㄨ繖閲屾垜浠皢榪涜灞忓箷緇樺埗銆?/font>
褰揟ICK_TIME = 40鏃訛紝閫昏緫鏇存柊甯х巼涓?5FPS錛屼絾鏄幇鍦ㄥぇ澶氭暟鏄懼崱閮借兘鏀寔鏇撮珮鐨勬覆鏌撳撫鐜囷紝鎵浠ユ垜浠病蹇呰鍙嶈岄攣瀹氭覆鏌撳撫鐜囦負(fù)25FPS銆?/font>
鎴戜滑鍙互緇勭粐鎴戜滑鐨勪唬鐮侊紝璁╂垜浠兘澶熷鉤婊戠殑浠庝竴涓昏緫鐘舵佸埌涓嬩竴涓姸鎬佸仛鎻掑箋?/font>percentWithinTick涓轟竴涓?鍒?涔嬮棿鐨勬誕鐐規(guī)暟錛岃〃紺烘垜浠簲璇ュ悜涓嬩竴涓昏緫甯ц蛋澶氳繙銆?/font>
Every object in the game has certain state regarding LastTick and NextTick. Each object that can move will have a LastPosition and a NextPosition. When the logic section updates the object, then the objects LastPosition is set to it's currentNextPostion and a new NextPosition is calculated based on how far it can move in one logic frame. Then, when the rendering portion executes, percentWithinTick is used to interpolate between these Last and Next positions:
姣忎釜鍙Щ鍔ㄧ殑瀵硅薄閮芥湁LastPosition鍜孨extPosition銆傞昏緫鏇存柊閮ㄥ垎鐨勪唬鐮佸湪鎵ц鏃朵細(xì)灝嗗璞$殑LastPosition璁劇疆涓哄綋鍓嶇殑NextPosition錛屽啀鏍規(guī)嵁瀹冩瘡涓甯ф墍鑳界Щ鍔ㄧ殑璺濈鏉ヨ綆楀嚭NextPosition銆?/font>
鐒跺悗錛屽湪娓叉煋瀵硅薄鐨勬椂鍊欙紝鍙互鍐嶇敤percentWithTick鏉ュ湪Last鍜孨ext浣嶇疆涔嬮棿榪涜鎻掑箋?/font>
璇戞敞錛?/font>
time0涓烘渶鍚庝竴涓昏緫甯ф墍鍦ㄧ殑鏃墮棿鐐癸紝time1涓哄綋鍓嶅疄闄呮椂闂達(dá)紝(time1 – time0) / TICK_TIME琛ㄧず褰撳墠鏃墮棿姣旀渶鍚庝竴涓昏緫甯ф墍鍦ㄦ椂闂磋秴鍑轟簡(jiǎn)澶氬皯鐧懼垎姣旓紝鐢ㄨ繖涓櫨鍒嗘瘮鏉ュ悜涓嬩竴閫昏緫甯у仛鎻掑箋?/font>
濡傛灉鏈哄櫒姣旇緝蹇紝鍦ㄤ袱涓昏緫鏇存柊鏃墮棿鐐逛箣闂存墽琛屼簡(jiǎn)澶氭灞忓箷娓叉煋錛岃繖鏍鋒彃鍊煎氨鏈夋晥浜?jiǎn)銆傛鏃秚ime0涓嶅彉錛宼ime1涓鐩村湪澧為暱錛屽彲浠ユ牴鎹闀跨殑鐧懼垎姣旀潵璁$畻鍔ㄧ敾搴旇浠庢渶鍚庝竴嬈℃墽琛岄昏緫鏇存柊鐨勪綅緗悜涓嬩竴嬈¢昏緫鏇存柊鎵鍦ㄧ殑浣嶇疆璧板榪溿?/font>
涔熷氨鏄笂鏂囨墍璇寸殑錛屽湪Last鍜孨ext浣嶇疆涔嬮棿榪涜鎻掑箋傛彃鍊煎悗鐨勫疄闄呬綅緗紝鍗矰rawPosition鐨勮綆楁柟娉曞涓嬶細(xì)
鍔ㄧ敾鎾斁鐨勬彃鍊間篃鏄被浼箋?/font>
DrawPosition = LastPosition + percentWithinTick * (NextPosition - LastPosition);
The main loop executes over and over as fast as the computer is able to run it, and for a lot of the time we will not be performing logic updates. During these loop cycles when no logic is performed, we can still draw, and as the loop progresses closer to the time of our next logic update, percentWithinTick will increase toward 1. The closer percentWithinTick gets to 1, the closer the a ctual DrawPosition of the object will get to NextPosition. The effect is that the object smoothly moves from Last to Next on the screen, the animation as smooth as the hardware framerate will allow. Without this smooth interpolation, the object would move in a jerk from LastPosition to NextPosition, each time the logic updates at 25FPS. So a lot of drawing cycles would be wasted repeatedly drawing the same exact image over and over, and the animation would be locked to a 25FPS rate that would look bad.
娓告垙鐨勪富寰幆浠ョ數(shù)鑴戞墍鑳藉杈懼埌鐨勬渶澶ч熷害涓嶅仠鍦拌繍琛岋紝鍦ㄥぇ澶氭暟鏃墮棿閲岋紝鎴戜滑灝嗕笉闇瑕佹墽琛岄昏緫鏇存柊銆?/font>
浣嗘槸鍦ㄨ繖浜涗笉鎵ц閫昏緫鏇存柊鐨勫懆鏈熼噷錛屾垜浠粛鐒跺彲浠ユ墽琛屽睆騫曟覆鏌撱傚綋寰幆鐨勮繘紼嬭秺鎺ヨ繎鎴戜滑鐨勪笅涓嬈¢昏緫鏇存柊鏃墮棿錛宲ercentWithinTick灝辨帴榪?錛沺ercentWithinTick瓚婃帴榪?錛孌rawPosition鐨勪綅緗氨瓚婃帴榪慛extPosition銆?/font>
鏈鍚庣殑鏁堟灉灝辨槸娓告垙瀵硅薄鍦ㄥ睆騫曚笂鎱㈡參鐨勩佸鉤婊戝湴浠嶭ast浣嶇疆縐誨姩鍒癗ext浣嶇疆錛屽姩浣滃皢浠ョ‖浠跺撫鐜囨墍鑳藉厑璁哥殑紼嬪害灝藉彲鑳界殑騫蟲粦銆?/font>
濡傛灉娌℃湁榪欎釜騫蟲粦鎻掑艱繃紼嬶紝瀵硅薄浣嶇疆灝嗕細(xì)浠庝笂涓甯ф墍鍦ㄧ殑LastPosition鐩存帴璺沖埌涓嬩竴甯ф墍鍦ㄧ殑浣嶇疆NextPosition銆傚ぇ閲忕殑娓叉煋鍛ㄦ湡閮借嫻垂鍦ㄦ妸瀵硅薄鍙嶅娓叉煋鍒扮浉鍚岀殑浣嶇疆涓婏紝騫朵笖鍔ㄧ敾涔熷彧鑳借閿佸畾鍦?5FPS錛屼嬌寰楃湅璧鋒潵鏁堟灉闈炲父宸?/font>
With this technique, logic and drawing are separated. It is possible to perform updates as infrequently as 14 or 15 times per second, far below the threshold necessary for smooth, decent looking visual framerate, yet still maintain the smooth framerate due to interpolation. Low logic update rates such as this are common in games such as real-time strategy games, where logic can eat up a lot of time in pathfinding and AI calculations that would choke a higher rate. Yet, the game will still animate smoothly from logic state to logic state, without the annoying visual hitches of a 15FPS visual framerate. Pretty danged nifty, I must say.
浣跨敤浜?jiǎn)杩櫃逡?guī)妧鏈箣鍚庯紝閫昏緫鏇存柊涓庡睆騫曟覆鏌撹鍒嗙寮浜?jiǎn)銆?/font>
榪欏皢鍏佽鎴戜滑鎶婇昏緫鏇存柊甯х巼闄嶄綆鍒?4鎴?5FPS錛岃繖榪滆繙浣庝簬騫蟲粦鐨勫姩鐢繪覆鏌撴墍闇瑕佺殑甯х巼錛屼絾鏄湪浣跨敤鍔ㄧ敾鎻掑間箣鍚庡嵈浠嶇劧鑳界淮鎸佸鉤婊戠殑娓叉煋甯х巼銆?/font>
鍦ㄥ疄鏃剁瓥鐣ョ被娓告垙涓彲鑳戒細(xì)浣跨敤榪欐牱浣庣殑閫昏緫鏇存柊甯х巼錛岃繖閲岄昏緫鏇存柊浼?xì)鍥犲璧\鍜孉I璁$畻鑰屽崰鐢ㄥぇ閲忕殑鏃墮棿錛屽湪榪欑鎯呭喌涓嬩嬌鐢ㄩ珮鐨勯昏緫鏇存柊甯х巼鏄劇劧涓嶈銆?/font>
浣嗘槸錛屾父鎴忓嵈浠嶇劧鑳藉鍦ㄤ笉鍚岀殑閫昏緫鐘舵佷箣闂村仛騫蟲粦鐨勫姩鐢昏繃娓★紝涓嶄細(xì)鍥犱負(fù)鍙湁15FPS鐨勬覆鏌撳撫鐜囪屽嚭鐜伴偅浜涗護(hù)浜虹敓鍘岀殑鍔ㄧ敾璺寵穬鐜拌薄銆?/font>
闈炲父鐨勬紓浜紝鎴戜笉寰椾笉璇淬?/font>
I've created a simple program in C to demonstrate this loop structure. It requires SDL ( http://www.libsdl.org ) and OpenGL. It's a basic bouncy ball program. The controls are simple: Press q to exit the program or press SPACE to toggle interpolation on and off. With interpolation on, the loop executes exactly as described to smooth out the movement from one logic update to the next. With interpolation off, the interpolation factor percentWithinTick is always set to 1 to simulate drawing without interpolation, in essence locking the visual framerate to the 25FPS of the logic update section. In both cases, the ball moves at exactly the same speed (16 units per update, 25 updates per second), but with interpolation the motion is much smoother and easier on the eyes. Compile and link with SDL and OpenGL to see it in action: http://legion.gibbering.net/golem/files/interp_demo.c