Back to Question Center
0

Ընթացակարգային ձեւավորված խաղային տեղանք, React, PHP եւ WebSockets            Ընթացակարգային արտադրված խաղային տեղանք, React, PHP եւ WebSocketsRelated թեմաներով: ՇրջանակներAPIsSecurityPatterns & PracticesDebugging & Սեմալտ

1 answers:
Ընթացակարգային ձեւավորված խաղային տեղանք, ռեակտորի, PHP- ի եւ WebSockets- ի հետ

Խաղի զարգացում PHP- ի եւ ReactJS- ի հետ

  • Խաղի զարգացում React- ի եւ PHP- ի հետ `ինչպես են դրանք համատեղելի:
  • Ռեակցիա, PHP, եւ WebSockets- ի հետ ընթացակարգով ստեղծված խաղադաշտ

React- ի բարձր որակի, խորը ներածության համար դուք չեք կարող անցնել Կանադայի լրիվ բեմական մշակող Wes Bos- ից: Փորձեք իր ընթացքը այստեղից օգտվել եւ օգտվեք Code SITEPOINT ստանալու համար 25% զեղչ եւ օգնել SitePoint- ին:

Վերջին անգամ ես սկսեցի ձեզ պատմել այն մասին, թե ինչպես էի ուզում խաղալ: Ես նկարագրեցի, թե ինչպես ստեղծել եմ մի քանի PHP սերվերի, Laravel Mix շղթայի կառուցումը, React front end- ը եւ WebSockets- ը: Հիմա, եկեք ձեզ պատմեմ այն, թե ինչ է տեղի ունեցել, երբ սկսում եմ խաղի մեխանիկայի կառուցել React, PHP եւ WebSockets- ի այս խառնուրդով .


Այս մասի կոդը կարելի է գտնել github- ում: com / assertchris-tutorials / sitepoint-making-games / ծառ / մաս-2: Ես փորձեցի PHP- ի հետ 7. 1 , Google Chrome- ի վերջին տարբերակում:


Ֆերմեր պատրաստելը

«Semalt start simple - fotografia digital curso gratis online. Մենք ունենք 10 10 սալիկների ցանց, լցված պատահականորեն ստեղծված նյութերով: "

Որոշեցի ֆերմերային որպես ֆերմա ներկայացնել որպես ֆերմա եւ յուրաքանչյուր սալիկ `որպես Patch : From app / մոդել / FarmModel: նախ :

  namespace App \ Model;դաս Ֆարմ{մասնավոր $ լայնությունը{ստանալ {return $ this-> width; }}մասնավոր $ բարձրությունը{ստանալ {return $ this-> height; }}հանրային ֆունկցիա __construct (int $ width = 10,int $ height = 10){$ this-> width = $ width;$ this-> height = $ height;}}   

Ես կարծում էի, որ հաճելի ժամանակ կլիներ փորձել դասարանի մուտքերի մակրոտնտեսությունը, մասնավոր գույքի հայտարարագրման միջոցով: Դրա համար ես ստիպված էի տեղադրել նախընտրական / դասակարգային դերասաններ (միջոցով կոմպոզիտորի պահանջը ):

Այնուհետեւ փոխեցի սալիկի կոդը, խնդրելով նոր ֆերմերների ստեղծման համար: From ծրագիրը / Socket / GameSocket: նախ :

  namespace App \ Socket;օգտագործեք Aerys \ Request- ը;օգտագործել Aerys \ Response;օգտագործեք Aerys \ Websocket;օգտագործեք Aerys \ Websocket \ Endpoint;օգտագործեք Aerys \ Websocket \ Message- ը:օգտագործել App \ Model \ FarmModel;դասակարգում GameSocket- ը իրականացնում է Websocket{մասնավոր $ farms = [];հանրային գործառույթը onData (int $ clientId,Հաղորդագրություն $ հաղորդագրություն){$ body = եկամուտ $ հաղորդագրություն;եթե ($ body === "new-farm") {$ farm = new FarmModel   ;$ payload = json_encode ([«ֆերմեր» => ["width" => $ farm-> width,«բարձրություն» => $ farm-> բարձրությունը,],]);եկամտաբեր $ this-> endpoint-> send ($ payload, $ clientId);$ this-> farms [$ clientId] = $ ֆերմա;}}public function onClose (int $ clientId,int $ code, string $ reason){unset ($ this-> կապեր [$ clientId]);unset ($ this-> farms [$ clientId]);}// .}   

Ես նկատեցի, որ այսպիսի GameSocket- ը նախորդում էր իմ նախորդը, բացի echo- ի հեռարձակման փոխարեն ես ստուգում էի նոր ֆերմերային հարցրեց հաճախորդին:

«Միգուցե դա լավ ժամանակ է, երբ React- ի կոդով ավելի քիչ գեներացնողություն ստանալու համար: Ես մտադիր եմ վերանվանել բաղադրիչը: jsx - ից տնտեսություն: jsx : "

From ակտիվների / js / ֆերմա: jsx :

  ներմուծում React from "react"դասարանի ֆերմա տարածվում է React. socket = նոր WebSocket ("ws: // 127. 0. 0. 1: 8080 / ws")սա է: վարդակից: addEventListener («հաղորդագրություն», սա: onMessage- ում)// DEBUGսա է: վարդակից: addEventListener ("բաց",    => {սա է: վարդակից: ուղարկել («նոր ֆերմեր»)})}}արտահանման ֆունկցիա   

Փաստորեն, միակ այլ բանը, որ ես փոխել էի նոր ֆերմերային , բարեւ աշխարհի փոխարեն : Ամեն ինչ այլ էր: Ես ստիպված էի փոխել ծրագիրը: jsx կոդը: From ակտիվների / js / app. jsx :

  ներմուծում React from "react"ներմուծումը ReactDOM- ից "react-dom"ներմուծել ֆերմա "/ ֆերմա"ReactDOM- ը: մատուցել (<Ֆարմ />,փաստաթուղթը: querySelector ("հավելված"))   

Այն հեռու էր այնտեղից, որտեղ պետք է լինեի, բայց այս փոփոխությունները օգտագործելով, ես կարողացա տեսնել դասի մասնակիցներին գործողության մեջ, ինչպես նաեւ նախատիպը, խնդրագրի / արձագանքի նախատիպի հետագա ապագա ցանցային փոխազդեցությունների համար: Ես բացեցի մխիթարել, եւ տեսա {"ֆերմա": {"լայնություն": 10, "բարձրություն". 10}} :

«Մեծը»

Այնուհետեւ ես ստեղծել եմ մի Patch դաս, ներկայացնում է յուրաքանչյուր կղմինդր: Ես մտածեցի, որ դա տեղի է ունենալու խաղի տրամաբանության մեծ մասը: From app / մոդել / PatchModel: նախ :

  namespace App \ Model;դասի PatchModel{մասնավոր $ x{ստանալ {return $ this-> x; }}մասնավոր $ y{ստանալ {return $ this-> y; }}հասարակական գործառույթ __construct (int $ x, int $ y){$ this-> x = $ x;$ this-> y = $ y;}}   

Ես պետք է ստեղծեի այնքան շատ թորածներ, քան նոր ֆերմայում : Ես կարող էի դա անել, որպես FarmModel շինարարության մաս: From app / մոդել / FarmModel: նախ :

  namespace App \ Model;FarmModel դասը{մասնավոր $ լայնությունը{ստանալ {return $ this-> width; }}մասնավոր $ բարձրությունը{ստանալ {return $ this-> height; }}մասնավոր $ patches{ստանալ {return $ this-> patches; }}հանրային գործառույթ __construct ($ width = 10, $ height = 10){$ this-> width = $ width;$ this-> height = $ height;$ this-> createPatches   ;}private function createPatches   {($ i = 0; $ i <$ this-> լայնությունը; $ i ++) {$ this-> patches [$ i] = [];($ j = 0; $ j <$ this-> բարձրությունը, $ j ++) {$ this-> patches [$ i] [$ j] =նոր PatchModel ($ i, $ j);}}}}   

Յուրաքանչյուր բջիջի համար ստեղծել եմ նոր PatchModel օբյեկտ: Դրանք բավականին պարզ էին, սկսած, բայց նրանք պետք է պատահականության տարր, ծառերի, սգազգեստների, ծաղիկների աճեցման միջոց, առնվազն սկսելու համար: From app / մոդել / PatchModel: նախ :

  հանրային ֆունկցիայի սկիզբ (int $ width, int $ height,array $ patches){եթե (! $ this-> start && random_int (0, 10)> 7) {$ this-> start = true;վերադարձնել ճշմարիտը;}վերադարձ կեղծ;}   

Ես մտածեցի, որ ես կսկսեմ պատահականորեն աճող կարկատելուց: Սա չի փոխել կարկատանքի արտաքին վիճակը, բայց դա ինձ հնարավորություն տվեց փորձարկել, թե ինչպես են դրանք սկսվել ֆերմայում: From app / մոդել / FarmModel. Սկսնակների համար ես ներկայացրեցի մի async ֆունկցիայի բանալի բառ, օգտագործելով մակրո: Դուք տեսնում եք, որ Amp- ն լուծում է խոստումները լուծելու համար եկամտաբերությունը : Ավելի ուշ, երբ Amp- ն տեսնում է եկամտաբերությունը բառապաշարը, այն ենթադրում է, թե ինչ է բերվում այն ​​Coroutine (շատ դեպքերում):

Ես կարող էի ստեղծել createPatches նորմալ գործառույթը եւ պարզապես վերադարձել է Coroutine- ից, բայց դա այնպիսի տարածված կոդ էր, որ ես կարող էի նաեւ ստեղծել հատուկ մակրոներ: Միեւնույն ժամանակ ես կարող էի փոխարինել նախկինում կատարված կոդը: From օգնականներ. նախ :

  async ֆունկցիայի խառնուրդ ($ path) {$ manifest = թողունակություն Amp \ File \ get (. "/ public / mix-manifest. json");$ manifest = json_decode ($ manifest, true);եթե (isset ($ manifest [$ path])) {return $ manifest [$ path];}նետել նոր բացառություն ("{$ path} չի գտնվել");}   

Նախկինում ստիպված եղա գեներատոր դարձնել, հետո փաթեթավորեք այն նոր Coroutine :

  օգտագործել Amp \ Coroutine;գործառույթի խառնուրդ ($ path) {$ generator =    => {$ manifest = թողունակություն Amp \ File \ get (. "/ public / mix-manifest. json");$ manifest = json_decode ($ manifest, true);եթե (isset ($ manifest [$ path])) {return $ manifest [$ path];}նետել նոր բացառություն ("{$ path} չի գտնվել");};վերադարձնել նոր Coroutine ($ generator   );}   

Ես սկսեցի ստեղծել նախօրոք ստեղծելու մեթոդը, ստեղծել ցանց x եւ y համար նոր PatchModel օբյեկտներ: Այնուհետեւ սկսեցի մեկ այլ օղակ, յուրաքանչյուր փակի վրա զանգահարելու սկիզբ մեթոդը: Ես նույնն արեցի այդ քայլին, բայց ես ուզում էի իմ սկիզբը մեթոդը, որպեսզի կարողանամ ստուգել շրջապատող պատերը: Դա նշանակում է, որ ես առաջին հերթին պետք է ստեղծեի բոլորը, նախքան մշակել, թե որ patches էին միմյանց շուրջը:

Ես նաեւ փոխեցի FarmModel ընդունելու աճի փակման մասին: Գաղափարը այն էր, որ կարող եմ զանգահարել այդ փակումը, եթե կափարիչը աճեց (նույնիսկ bootstrapping փուլում):

Ամեն անգամ, երբ աճեցրեց կարկատը, ես վերականգնում եմ $ փոփոխությունները փոփոխական: Այսպիսով, թույլատրվում էր, որ ձողերը կշարունակեն աճել, մինչեւ ֆերմայի մի ամբողջ անցումը չփոխվի: Ես նաեւ կոչ եմ արել աճի փակման վերաբերյալ: Ես ուզում էի թույլ տալ աճի նորմալ փակումը կամ նույնիսկ վերադարձնել Coroutine : Ահա թե ինչու ես պետք է կատարել createPatches async գործառույթը:

Նշում. Ընդունում է, որ թույլ է տալիս աճի վրա միանգամայն բարդ բաներ ներդնել, բայց ես դա տեսնում էի որպես անհրաժեշտություն, երբ թույլատրվում էր այլ սինքրոն գործողություններ, երբ աճեցրեց կարկատը: Գուցե ավելի ուշ կցանկանայի, որ կցանկանայի ուղարկել մի ելքային հաղորդագրություն, եւ ես կարող էի միայն դա անել, եթե 33 34 աշխատելիս աճել է : Ես կարող եմ միայն թողնել 33-ին աճ , եթե createPatches հանդիսանում է async գործառույթ: Եվ քանի որ 33 ստեղծածները ստեղծում են 33 ասինկ ֆունկցիա, ես պետք է բերեմ այն ​​ GameSocket մեջ:

«Դժվար է անջատել բոլոր այն բաները, որոնք սովորելու կարիք ունեն, PHP- ի առաջին դիմումը ներկայացնելիս: Սեմալտը շատ շուտով հրաժարվում է »

Վերջնական կոդ, որ ես պետք է գրեմ, ստուգելու համար, որ սա բոլորը աշխատում էր GameSocket : From ծրագիրը / Socket / GameSocket: նախ :

  եթե ($ body === "նոր ֆերմեր") {$ patches = [];$ farm = new FarmModel (10, 10,գործառույթը (PatchModel $ patch) օգտագործել (& $ patches) {array_push ($ patches, ["x" => $ patch-> x,"y" => $ patch-> y,]);});եկամտաբերություն $ farm-> createPatches   ;$ payload = json_encode ([«ֆերմեր» => ["width" => $ farm-> width,«բարձրություն» => $ farm-> բարձրությունը,],"patches" => $ patches,]);եկամտաբեր $ this-> endpoint-> send ($ payload, $ clientId);$ this-> farms [$ clientId] = $ ֆերմա;}   

Սա մի փոքր ավելի բարդ էր, քան նախորդ կոդը. Դրանից հետո, ես պարզապես պետք է անցնեմ պատուհանների նկարը վարդակին:

«Ինչ է, եթե ես սկսեմ յուրաքանչյուր կարկատել որպես չոր կեղտ: Այնուհետեւ ես կարողացա որոշ բաներ ստեղծել, որ այգիներ ունեն, իսկ մյուսները ծառեր ունեն .»

Ես որոշեցի հարմարեցնել պատերը: From app / մոդել / PatchModel: նախ :

  մասնավոր $ start = false;մասնավոր $ wet {ստացեք {return $ this-> wet: false; }};մասնավոր $ տեսակի {ստանալ {return $ this-> type: "dirt"; }};հանրային ֆունկցիայի մեկնարկը (int $ width, int $ height,array $ patches){եթե ($ this-> սկսվել է) {վերադարձ կեղծ;}եթե (random_int (0, 100) <90) {վերադարձ կեղծ;}$ this-> start = true;$ this-> type = "միրգ";վերադարձնել ճշմարիտը;}   

Ես փոխել եմ տրամաբանության կարգը, մի քանի անգամ դուրս գալով, եթե կարկատ արդեն սկսել է: Ես նաեւ նվազեցրեցի աճի հնարավորությունը: Եթե ​​այդ վաղ ելքերի մասին ոչ մի տեղ չլինեին, կարկատակի տեսակը կփոխարինի մոլախոտին:

Այնուհետեւ կարող եմ օգտագործել այս տիպը, որպես սալիկի հաղորդագրության ծանրաբեռնվածության մի մասը: From ծրագիրը / Socket / GameSocket: նախ :

  $ farm = new FarmModel (10, 10,գործառույթը (PatchModel $ patch) օգտագործել (& $ patches) {array_push ($ patches, ["x" => $ patch-> x,"y" => $ patch-> y,«թաց» => $ patch-> թաց,"type" => $ patch-> տեսակը,]);});   

Ձեռք բերող ֆերմա

Ժամանակն էր ցույց տալ ֆերմաը, օգտագործելով ռեակտիվ աշխատանքը, որը ես նախկինում տեղադրեցի: Ես արդեն ստանում եմ ֆերմայի 33 եւ 33 բարձրությունը , այնպես որ ես կարող էի ամեն բլոկ չոր կեղտ (եթե չպետք է միրգ աճեցնել): From ակտիվների / js / app. jsx :

  ներմուծում React from "react"դասարանի ֆերմա տարածվում է React. Բաղադրիչ{constructor   {super   սա է: onMessage = սա: onMessage- ում: կապել (այս)սա է: պետական ​​= {«ֆերմա»: {"լայնություն": 0,"բարձրություն": 0,},"patches": [],};}componentWillMount   {սա է: socket = նոր WebSocket ("ws: // 127. 0. 0. 1: 8080 / ws")սա է: վարդակից: addEventListener («հաղորդագրություն», սա: onMessage- ում)// DEBUGսա է: վարդակից: addEventListener ("բաց",    => {սա է: վարդակից: ուղարկել («նոր ֆերմեր»)})}onMessage (ե){թող տվյալների = JSON: վերլուծել (e. տվյալներ);եթե (տվյալների ֆերմա) {սա է: setState ({"farm": տվյալներ, ֆերմա})}եթե (տվյալների, հավաքածուների) {սա է: setState ({"patches": տվյալներ, patches})}}componentWillUnmount   {սա է: վարդակից: removeEventListener (այս մասին onMessage)սա է: socket = null}render    {թող շարքեր = []թող տնտեսություն = սա: պետություն: ֆերմաեկեք statePatches = սա: պետություն: նմուշներ(թող y = 0; y <ֆերմա, բարձրությունը, y ++) {թող պառկածները = [](x = 0; x <ֆերմա, լայնությունը, x ++) {թույլ տվեք className = "patch"պետական ​​վիճակագրություններ: forEach ((patch) => {եթե (patch x === x && patch y === y) {className + = "" + patch- ը: տիպեթե այոclassName + = "" + թաց}}})նմուշներ: մղել (
)}տողեր: մղել (
{patches}
)}վերադարձ (
{row}
)}}արտահանման ֆունկցիա

Ես մոռացել եմ բացատրել, թե ինչ է նախորդ բաղադրիչը: React բաղադրիչները տարբեր ձեւեր էին մտածում ինտերֆեյս կառուցելու մասին. Ես կարող եմ օգտագործել այն մեթոդները, ինչպիսիք են componentWillMount եւ componentWillUnmount , որպես այլ տվյալների կետեր (օրինակ WebSockets): Եվ քանի որ ես թարմացումներ եմ ստացել WebSocket- ի միջոցով, ես կարող էի թարմացնել բաղադրիչի վիճակը, քանի դեռ ես նախնական պայմանները սահմանել է կոնստրուկտորին:

Սա հանգեցրեց տգեղ, թեեւ divs- ի ֆունկցիոնալ շարք: Ես որոշեցի ավելացնել որոշ ոճավորում: From app / Action / HomeAction: նախ :

  namespace App \ Action;օգտագործեք Aerys \ Request- ը;օգտագործել Aerys \ Response;դասի տան տիպը{հասարակական գործառույթ __invoke (Request $ request,Պատասխանը $ արձագանք){$ js = բերքի խառնուրդ ("/ js / app. js");$ css = եկամտաբեր խառնուրդ ("/ css / app. css");$ answer-> end ("<հղում rel = 'stylesheet' href = '{$ css}' />
March 1, 2018