[{"data":1,"prerenderedAt":731},["ShallowReactive",2],{"blog-canvas-player-input":3},{"id":4,"title":5,"body":6,"description":15,"extension":720,"meta":721,"navigation":135,"path":727,"seo":728,"stem":729,"__hash__":730},"blog\u002Fblog\u002Fcanvas-player-input.md","HTML5 Canvas: Handling Player Input (Keyboard & Mouse)",{"type":7,"value":8,"toc":718},"minimark",[9,714],[10,11,12,361],"i18n-text",{},[13,14,16,21,25,30,33,46,51,97,101,164,168,171,263,267,274,350],"template",{"v-slot:en":15},"",[17,18,20],"h1",{"id":19},"html5-canvas-handling-player-input","HTML5 Canvas: Handling Player Input",[22,23,24],"p",{},"A game without player input is just an animation. To make it a true game, we need to allow the player to control elements on the screen. Let's learn to capture keyboard and mouse input effectively!",[26,27,29],"h2",{"id":28},"_1-the-right-way-to-handle-keyboard-input","1. The Right Way to Handle Keyboard Input",[22,31,32],{},"If you simply attach an event listener to a button press and move the player directly inside that listener, the movement will feel choppy and clunky.",[22,34,35,36,40,41,45],{},"The industry-standard way to do this is to keep track of ",[37,38,39],"strong",{},"which keys are currently being held down",". We do this by storing the state in an object, and then applying movement inside our ",[42,43,44],"code",{},"requestAnimationFrame"," Game Loop.",[47,48,50],"h3",{"id":49},"step-1-create-a-state-object","Step 1: Create a State Object",[52,53,57],"pre",{"className":54,"code":55,"language":56,"meta":15,"style":15},"language-javascript shiki shiki-themes github-light github-dark","const keys = {\n  ArrowUp: false,\n  ArrowDown: false,\n  ArrowLeft: false,\n  ArrowRight: false\n};\n","javascript",[42,58,59,67,73,79,85,91],{"__ignoreMap":15},[60,61,64],"span",{"class":62,"line":63},"line",1,[60,65,66],{},"const keys = {\n",[60,68,70],{"class":62,"line":69},2,[60,71,72],{},"  ArrowUp: false,\n",[60,74,76],{"class":62,"line":75},3,[60,77,78],{},"  ArrowDown: false,\n",[60,80,82],{"class":62,"line":81},4,[60,83,84],{},"  ArrowLeft: false,\n",[60,86,88],{"class":62,"line":87},5,[60,89,90],{},"  ArrowRight: false\n",[60,92,94],{"class":62,"line":93},6,[60,95,96],{},"};\n",[47,98,100],{"id":99},"step-2-listeners-to-update-state","Step 2: Listeners to Update State",[52,102,104],{"className":54,"code":103,"language":56,"meta":15,"style":15},"window.addEventListener('keydown', (e) => {\n  if (keys.hasOwnProperty(e.code)) {\n    keys[e.code] = true; \u002F\u002F Key is being pressed\n  }\n});\n\nwindow.addEventListener('keyup', (e) => {\n  if (keys.hasOwnProperty(e.code)) {\n    keys[e.code] = false; \u002F\u002F Key was released\n  }\n});\n",[42,105,106,111,116,121,126,131,137,143,148,154,159],{"__ignoreMap":15},[60,107,108],{"class":62,"line":63},[60,109,110],{},"window.addEventListener('keydown', (e) => {\n",[60,112,113],{"class":62,"line":69},[60,114,115],{},"  if (keys.hasOwnProperty(e.code)) {\n",[60,117,118],{"class":62,"line":75},[60,119,120],{},"    keys[e.code] = true; \u002F\u002F Key is being pressed\n",[60,122,123],{"class":62,"line":81},[60,124,125],{},"  }\n",[60,127,128],{"class":62,"line":87},[60,129,130],{},"});\n",[60,132,133],{"class":62,"line":93},[60,134,136],{"emptyLinePlaceholder":135},true,"\n",[60,138,140],{"class":62,"line":139},7,[60,141,142],{},"window.addEventListener('keyup', (e) => {\n",[60,144,146],{"class":62,"line":145},8,[60,147,115],{},[60,149,151],{"class":62,"line":150},9,[60,152,153],{},"    keys[e.code] = false; \u002F\u002F Key was released\n",[60,155,157],{"class":62,"line":156},10,[60,158,125],{},[60,160,162],{"class":62,"line":161},11,[60,163,130],{},[47,165,167],{"id":166},"step-3-apply-to-game-loop","Step 3: Apply to Game Loop",[22,169,170],{},"Now, inside the same Game Loop we learned about previously!",[52,172,174],{"className":54,"code":173,"language":56,"meta":15,"style":15},"let player = { x: 100, y: 100, size: 30, speed: 5 };\n\nfunction update() {\n  ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n  \u002F\u002F Smooth movement checked every single frame!\n  if (keys.ArrowUp) player.y -= player.speed;\n  if (keys.ArrowDown) player.y += player.speed;\n  if (keys.ArrowLeft) player.x -= player.speed;\n  if (keys.ArrowRight) player.x += player.speed;\n\n  \u002F\u002F Draw Player\n  ctx.fillStyle = 'blue';\n  ctx.fillRect(player.x, player.y, player.size, player.size);\n\n  requestAnimationFrame(update);\n}\n",[42,175,176,181,185,190,195,199,204,209,214,219,224,228,234,240,246,251,257],{"__ignoreMap":15},[60,177,178],{"class":62,"line":63},[60,179,180],{},"let player = { x: 100, y: 100, size: 30, speed: 5 };\n",[60,182,183],{"class":62,"line":69},[60,184,136],{"emptyLinePlaceholder":135},[60,186,187],{"class":62,"line":75},[60,188,189],{},"function update() {\n",[60,191,192],{"class":62,"line":81},[60,193,194],{},"  ctx.clearRect(0, 0, canvas.width, canvas.height);\n",[60,196,197],{"class":62,"line":87},[60,198,136],{"emptyLinePlaceholder":135},[60,200,201],{"class":62,"line":93},[60,202,203],{},"  \u002F\u002F Smooth movement checked every single frame!\n",[60,205,206],{"class":62,"line":139},[60,207,208],{},"  if (keys.ArrowUp) player.y -= player.speed;\n",[60,210,211],{"class":62,"line":145},[60,212,213],{},"  if (keys.ArrowDown) player.y += player.speed;\n",[60,215,216],{"class":62,"line":150},[60,217,218],{},"  if (keys.ArrowLeft) player.x -= player.speed;\n",[60,220,221],{"class":62,"line":156},[60,222,223],{},"  if (keys.ArrowRight) player.x += player.speed;\n",[60,225,226],{"class":62,"line":161},[60,227,136],{"emptyLinePlaceholder":135},[60,229,231],{"class":62,"line":230},12,[60,232,233],{},"  \u002F\u002F Draw Player\n",[60,235,237],{"class":62,"line":236},13,[60,238,239],{},"  ctx.fillStyle = 'blue';\n",[60,241,243],{"class":62,"line":242},14,[60,244,245],{},"  ctx.fillRect(player.x, player.y, player.size, player.size);\n",[60,247,249],{"class":62,"line":248},15,[60,250,136],{"emptyLinePlaceholder":135},[60,252,254],{"class":62,"line":253},16,[60,255,256],{},"  requestAnimationFrame(update);\n",[60,258,260],{"class":62,"line":259},17,[60,261,262],{},"}\n",[26,264,266],{"id":265},"_2-tracking-the-mouse","2. Tracking the Mouse",[22,268,269,270,273],{},"Mouse tracking is incredibly useful for shooting games, drag-and-drop puzzles, or custom cursors.\nBecause the standard ",[42,271,272],{},"e.clientX"," gives the position based on the whole browser window, we must subtract the canvas's padding\u002Fmargin bounds to get the exact X and Y on our game grid.",[52,275,277],{"className":54,"code":276,"language":56,"meta":15,"style":15},"let mouse = { x: 0, y: 0 };\n\ncanvas.addEventListener('mousemove', (e) => {\n  \u002F\u002F Get the size and position of the canvas on the webpage\n  const rect = canvas.getBoundingClientRect(); \n  \n  \u002F\u002F Calculate exact coordinates relative to canvas\n  mouse.x = e.clientX - rect.left;\n  mouse.y = e.clientY - rect.top;\n});\n\n\u002F\u002F Want a click event?\ncanvas.addEventListener('click', (e) => {\n  console.log(`Pew pew! Gun fired at X: ${mouse.x}, Y: ${mouse.y}`);\n});\n",[42,278,279,284,288,293,298,303,308,313,318,323,327,331,336,341,346],{"__ignoreMap":15},[60,280,281],{"class":62,"line":63},[60,282,283],{},"let mouse = { x: 0, y: 0 };\n",[60,285,286],{"class":62,"line":69},[60,287,136],{"emptyLinePlaceholder":135},[60,289,290],{"class":62,"line":75},[60,291,292],{},"canvas.addEventListener('mousemove', (e) => {\n",[60,294,295],{"class":62,"line":81},[60,296,297],{},"  \u002F\u002F Get the size and position of the canvas on the webpage\n",[60,299,300],{"class":62,"line":87},[60,301,302],{},"  const rect = canvas.getBoundingClientRect(); \n",[60,304,305],{"class":62,"line":93},[60,306,307],{},"  \n",[60,309,310],{"class":62,"line":139},[60,311,312],{},"  \u002F\u002F Calculate exact coordinates relative to canvas\n",[60,314,315],{"class":62,"line":145},[60,316,317],{},"  mouse.x = e.clientX - rect.left;\n",[60,319,320],{"class":62,"line":150},[60,321,322],{},"  mouse.y = e.clientY - rect.top;\n",[60,324,325],{"class":62,"line":156},[60,326,130],{},[60,328,329],{"class":62,"line":161},[60,330,136],{"emptyLinePlaceholder":135},[60,332,333],{"class":62,"line":230},[60,334,335],{},"\u002F\u002F Want a click event?\n",[60,337,338],{"class":62,"line":236},[60,339,340],{},"canvas.addEventListener('click', (e) => {\n",[60,342,343],{"class":62,"line":242},[60,344,345],{},"  console.log(`Pew pew! Gun fired at X: ${mouse.x}, Y: ${mouse.y}`);\n",[60,347,348],{"class":62,"line":248},[60,349,130],{},[22,351,352,353,356,357,360],{},"Now you can build a game loop that constantly draws an crosshair image exactly at ",[42,354,355],{},"mouse.x"," and ",[42,358,359],{},"mouse.y","!",[13,362,363,367,378,382,393,407,411,445,449,511,515,518,606,610,617,623,701],{"v-slot:id":15},[17,364,366],{"id":365},"html5-canvas-menangani-input-pemain","HTML5 Canvas: Menangani Input Pemain",[22,368,369,370,374,375,360],{},"Sebuah game tanpa input kendali pemain tidak lebih dari sekadar pemutaran video animasi. Agar ia bisa secara resmi divalidasi menjadi game yang interaktif, kita memerlukan izin perintah supaya para pemain dapat ikut menavigasi elemen layar. Mari pelajari cara tak terkalahkan dalam merekam ketukan interaksi tombol ",[371,372,373],"em",{},"Keyboard"," maupun sasaran ",[371,376,377],{},"Mouse",[26,379,381],{"id":380},"_1-cara-profesional-menangani-tombol-keyboard","1. Cara Profesional Menangani Tombol Keyboard",[22,383,384,385,388,389,392],{},"Satu kesalahan besar paling fatal mayoritas pemula adalah langsung \"menempelkan\" rumus matematika penambahan jarak gerakan berjalan (contoh iseng misal: ",[42,386,387],{},"player.y -= 5",") dicangkok mentah bulat layaknya teks di dalam fungsi interupsi ",[42,390,391],{},"keydown",". Efek buruknya? Gerakan tokoh protagonis buatan Anda akan nge-lag tersentak kaku bagai patah-pahat menunda, didera sistem sistem Operasi OS pengetik keyboard komputer.",[22,394,395,396,399,400,403,404,406],{},"Teknik kerahasiaan pabrik dari Industri \u002F Produsen Game Standart Dunia adalah dengan merubah pendekatan itu; mendata riwayat catatan jejak ",[37,397,398],{},"\"Tuts apa sajakah yang berstatus Sedang-Digenggam oleh Tekanan Jari Pemain SAAT INI?\"",". Data set status tersebut dihimpun sebagai Object di RAM, dan proses pemindahan langkah diserahkan langsung ke ",[371,401,402],{},"Game Loop"," beruntun setiap detiknya (",[42,405,44],{},") biar pergerakan terlihat lumer semulus sutra.",[47,408,410],{"id":409},"langkah-1-siapkan-keranjang-object-status-tombol","Langkah 1: Siapkan Keranjang Object Status Tombol",[52,412,414],{"className":54,"code":413,"language":56,"meta":15,"style":15},"const tombolArah = {\n  ArrowUp: false,    \u002F\u002F Tombol Atas \n  ArrowDown: false,  \u002F\u002F Tombol Bawah\n  ArrowLeft: false,  \u002F\u002F Tombol Kiri\n  ArrowRight: false  \u002F\u002F Tombol Kanan\n};\n",[42,415,416,421,426,431,436,441],{"__ignoreMap":15},[60,417,418],{"class":62,"line":63},[60,419,420],{},"const tombolArah = {\n",[60,422,423],{"class":62,"line":69},[60,424,425],{},"  ArrowUp: false,    \u002F\u002F Tombol Atas \n",[60,427,428],{"class":62,"line":75},[60,429,430],{},"  ArrowDown: false,  \u002F\u002F Tombol Bawah\n",[60,432,433],{"class":62,"line":81},[60,434,435],{},"  ArrowLeft: false,  \u002F\u002F Tombol Kiri\n",[60,437,438],{"class":62,"line":87},[60,439,440],{},"  ArrowRight: false  \u002F\u002F Tombol Kanan\n",[60,442,443],{"class":62,"line":93},[60,444,96],{},[47,446,448],{"id":447},"langkah-2-event-listener-sebagai-radar-pendengar","Langkah 2: Event Listener Sebagai Radar Pendengar",[52,450,452],{"className":54,"code":451,"language":56,"meta":15,"style":15},"\u002F\u002F Kala tuas Keyboard disentuh turun di genggam jari\nwindow.addEventListener('keydown', (e) => {\n  if (tombolArah.hasOwnProperty(e.code)) {\n    tombolArah[e.code] = true; \u002F\u002F Setir gas mesin berstatus MENYALA (true)\n  }\n});\n\n\u002F\u002F Kala beban genggam jari cabut melepaskan kaitan dari tuasnya\nwindow.addEventListener('keyup', (e) => {\n  if (tombolArah.hasOwnProperty(e.code)) {\n    tombolArah[e.code] = false; \u002F\u002F Rem ditarik otomatis\u002FMatikan gas pacu lari (false)\n  }\n});\n",[42,453,454,459,463,468,473,477,481,485,490,494,498,503,507],{"__ignoreMap":15},[60,455,456],{"class":62,"line":63},[60,457,458],{},"\u002F\u002F Kala tuas Keyboard disentuh turun di genggam jari\n",[60,460,461],{"class":62,"line":69},[60,462,110],{},[60,464,465],{"class":62,"line":75},[60,466,467],{},"  if (tombolArah.hasOwnProperty(e.code)) {\n",[60,469,470],{"class":62,"line":81},[60,471,472],{},"    tombolArah[e.code] = true; \u002F\u002F Setir gas mesin berstatus MENYALA (true)\n",[60,474,475],{"class":62,"line":87},[60,476,125],{},[60,478,479],{"class":62,"line":93},[60,480,130],{},[60,482,483],{"class":62,"line":139},[60,484,136],{"emptyLinePlaceholder":135},[60,486,487],{"class":62,"line":145},[60,488,489],{},"\u002F\u002F Kala beban genggam jari cabut melepaskan kaitan dari tuasnya\n",[60,491,492],{"class":62,"line":150},[60,493,142],{},[60,495,496],{"class":62,"line":156},[60,497,467],{},[60,499,500],{"class":62,"line":161},[60,501,502],{},"    tombolArah[e.code] = false; \u002F\u002F Rem ditarik otomatis\u002FMatikan gas pacu lari (false)\n",[60,504,505],{"class":62,"line":230},[60,506,125],{},[60,508,509],{"class":62,"line":236},[60,510,130],{},[47,512,514],{"id":513},"langkah-3-eksekusi-suntik-masal-ke-jantung-game-loop","Langkah 3: Eksekusi Suntik Masal ke Jantung Game Loop!",[22,516,517],{},"Mari gabungkan rumus radar tersebut di mesin \"Game Loop Pembaruan Frame\" (materi seri kanvas terakhir yang kita pelajari tempo lampau)!",[52,519,521],{"className":54,"code":520,"language":56,"meta":15,"style":15},"let player = { x: 100, y: 100, size: 30, speed: 5 };\n\nfunction pembaruanGame() {\n  ctx.clearRect(0, 0, canvas.width, canvas.height); \u002F\u002F Hukum wajib No.1: Sapu layarnya!\n\n  \u002F\u002F UPDATE LOGIKA GERAKAN SUPREME 60 FPS YANG SUPER LICIN :\n  \u002F\u002F Dicek kondisi radarnya, dan paksakan digeser terus menerus setiap satu hela Frame berjalan!\n  if (tombolArah.ArrowUp) player.y -= player.speed;\n  if (tombolArah.ArrowDown) player.y += player.speed;\n  if (tombolArah.ArrowLeft) player.x -= player.speed;\n  if (tombolArah.ArrowRight) player.x += player.speed;\n\n  \u002F\u002F Tahap Cetak Render Kotak Karakter Pemain di pos terbaru.\n  ctx.fillStyle = 'blue';\n  ctx.fillRect(player.x, player.y, player.size, player.size);\n\n  requestAnimationFrame(pembaruanGame);\n}\n",[42,522,523,527,531,536,541,545,550,555,560,565,570,575,579,584,588,592,596,601],{"__ignoreMap":15},[60,524,525],{"class":62,"line":63},[60,526,180],{},[60,528,529],{"class":62,"line":69},[60,530,136],{"emptyLinePlaceholder":135},[60,532,533],{"class":62,"line":75},[60,534,535],{},"function pembaruanGame() {\n",[60,537,538],{"class":62,"line":81},[60,539,540],{},"  ctx.clearRect(0, 0, canvas.width, canvas.height); \u002F\u002F Hukum wajib No.1: Sapu layarnya!\n",[60,542,543],{"class":62,"line":87},[60,544,136],{"emptyLinePlaceholder":135},[60,546,547],{"class":62,"line":93},[60,548,549],{},"  \u002F\u002F UPDATE LOGIKA GERAKAN SUPREME 60 FPS YANG SUPER LICIN :\n",[60,551,552],{"class":62,"line":139},[60,553,554],{},"  \u002F\u002F Dicek kondisi radarnya, dan paksakan digeser terus menerus setiap satu hela Frame berjalan!\n",[60,556,557],{"class":62,"line":145},[60,558,559],{},"  if (tombolArah.ArrowUp) player.y -= player.speed;\n",[60,561,562],{"class":62,"line":150},[60,563,564],{},"  if (tombolArah.ArrowDown) player.y += player.speed;\n",[60,566,567],{"class":62,"line":156},[60,568,569],{},"  if (tombolArah.ArrowLeft) player.x -= player.speed;\n",[60,571,572],{"class":62,"line":161},[60,573,574],{},"  if (tombolArah.ArrowRight) player.x += player.speed;\n",[60,576,577],{"class":62,"line":230},[60,578,136],{"emptyLinePlaceholder":135},[60,580,581],{"class":62,"line":236},[60,582,583],{},"  \u002F\u002F Tahap Cetak Render Kotak Karakter Pemain di pos terbaru.\n",[60,585,586],{"class":62,"line":242},[60,587,239],{},[60,589,590],{"class":62,"line":248},[60,591,245],{},[60,593,594],{"class":62,"line":253},[60,595,136],{"emptyLinePlaceholder":135},[60,597,598],{"class":62,"line":259},[60,599,600],{},"  requestAnimationFrame(pembaruanGame);\n",[60,602,604],{"class":62,"line":603},18,[60,605,262],{},[26,607,609],{"id":608},"_2-kalkulasi-koordinat-bidikan-tracking-target-mouse","2. Kalkulasi Koordinat Bidikan (Tracking Target Mouse)",[22,611,612,613,616],{},"Pengejaran koordinat kursor sentuhan ujung Tikus (Mouse) adalah kunci nyawa bagi genre peluru senapan (",[371,614,615],{},"Shooting crosshairs","), bongkar pasang Puzzle layaknya Drag And Drop, maupun elemen strategi menyorot unit (RTS).",[22,618,619,620,622],{},"Mengingat rumus variabel mentah bawaan browser JavaScript itu ",[42,621,272],{}," yang hanya melaporkan perhitungan asal mula sentimeter dari pojok tepian kaca browser, kita DITUNTUT WAJIB mutlak memotong tepian garis bingkai kanvas (Border\u002FMargin) untuk mengail koordinat murni dari arena dalam Canvas.",[52,624,626],{"className":54,"code":625,"language":56,"meta":15,"style":15},"let kordinatPusatMouse = { x: 0, y: 0 };\n\n\u002F\u002F Taruh radar di seluruh body Kanvas perihal pergeseran sekecil apapun\ncanvas.addEventListener('mousemove', (e) => {\n  \u002F\u002F Bantuan Penggaris Gaib JavaScript menilik besaran bingkai kanvas\n  const ukuranBingkaiGame = canvas.getBoundingClientRect(); \n  \n  \u002F\u002F Penggabungan titik selisih, tembus menuju arena titik Asli!\n  kordinatPusatMouse.x = e.clientX - ukuranBingkaiGame.left;\n  kordinatPusatMouse.y = e.clientY - ukuranBingkaiGame.top;\n});\n\n\u002F\u002F Ingin mekanisme melepaskan peluru ketika Jari nge-KLIK kiri kursor senapannya?\ncanvas.addEventListener('click', (e) => {\n  console.log(`DARR! Pistol meledak mencuat memuntahkan peluru pelacak tepat ke kordinat Sasaran Musuh X: ${kordinatPusatMouse.x}, Y: ${kordinatPusatMouse.y}`);\n});\n",[42,627,628,633,637,642,646,651,656,660,665,670,675,679,683,688,692,697],{"__ignoreMap":15},[60,629,630],{"class":62,"line":63},[60,631,632],{},"let kordinatPusatMouse = { x: 0, y: 0 };\n",[60,634,635],{"class":62,"line":69},[60,636,136],{"emptyLinePlaceholder":135},[60,638,639],{"class":62,"line":75},[60,640,641],{},"\u002F\u002F Taruh radar di seluruh body Kanvas perihal pergeseran sekecil apapun\n",[60,643,644],{"class":62,"line":81},[60,645,292],{},[60,647,648],{"class":62,"line":87},[60,649,650],{},"  \u002F\u002F Bantuan Penggaris Gaib JavaScript menilik besaran bingkai kanvas\n",[60,652,653],{"class":62,"line":93},[60,654,655],{},"  const ukuranBingkaiGame = canvas.getBoundingClientRect(); \n",[60,657,658],{"class":62,"line":139},[60,659,307],{},[60,661,662],{"class":62,"line":145},[60,663,664],{},"  \u002F\u002F Penggabungan titik selisih, tembus menuju arena titik Asli!\n",[60,666,667],{"class":62,"line":150},[60,668,669],{},"  kordinatPusatMouse.x = e.clientX - ukuranBingkaiGame.left;\n",[60,671,672],{"class":62,"line":156},[60,673,674],{},"  kordinatPusatMouse.y = e.clientY - ukuranBingkaiGame.top;\n",[60,676,677],{"class":62,"line":161},[60,678,130],{},[60,680,681],{"class":62,"line":230},[60,682,136],{"emptyLinePlaceholder":135},[60,684,685],{"class":62,"line":236},[60,686,687],{},"\u002F\u002F Ingin mekanisme melepaskan peluru ketika Jari nge-KLIK kiri kursor senapannya?\n",[60,689,690],{"class":62,"line":242},[60,691,340],{},[60,693,694],{"class":62,"line":248},[60,695,696],{},"  console.log(`DARR! Pistol meledak mencuat memuntahkan peluru pelacak tepat ke kordinat Sasaran Musuh X: ${kordinatPusatMouse.x}, Y: ${kordinatPusatMouse.y}`);\n",[60,698,699],{"class":62,"line":253},[60,700,130],{},[22,702,703,704,707,708,713],{},"Dan dengan rumus senjata tersebut di tangan, impian merakit gambar logo sasaran Tembak atau siluet objek buatan memanjang (",[371,705,706],{},"Custom Cursor Image","), sekarang otomatis terpacaki mengekor mengikuti di setiap sumbu lari ",[371,709,710],{},[42,711,712],{},"kordinatPusatMouse.x"," !",[715,716,717],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":15,"searchDepth":69,"depth":69,"links":719},[],"md",{"title_en":5,"title_id":722,"description_en":723,"description_id":724,"date":725,"readingTime":726},"HTML5 Canvas: Menangani Input Pemain (Keyboard & Mouse)","Learn how to make your game interactive by capturing keyboard strokes for smooth movement and tracking mouse coordinates.","Pelajari cara membuat game Anda interaktif dengan mendengarkan input tombol keyboard dan melacak koordinat mouse pemain.","2026-03-25","7 min read","\u002Fblog\u002Fcanvas-player-input",{"title":5,"description":15},"blog\u002Fcanvas-player-input","QGiFmUpdifNaVcBkqvBvfYZ5fFqXvw3bH-jLssDyJrs",1783751390704]