sample\sample_ezphp\
sample03_ezphp-sql.hsp
#include "hsp3dish.as" #include "ezlocal-dish-js.hsp" ezLocalJS #include "mod_ezphp.hsp" ; ; ブロック崩し(3) ; (ボール・バー・ブロック表示します) ; ; ezphpsql によるデータベース編集 - 319行目からを参照してください。 ; #cmpopt varname 1 x1=0:y1=0 ; 画面左上の座標 x2=ginfo_winx:y2=ginfo_winy ; 画面右下の座標 ;screen 0,x2,y2 title "ブロック崩し" ;cls 4 *gstart clrflag=0 ; クリアフラグ blsize=16 ; ボールのサイズ blspd=40 ; ボールのスピード blx=200:bly=200 ; ボールの座標 bpx=4:bpy=4 ; XY方向のボール座標加算値 bk=0 ; ブロックを崩すフラグ(1=崩す) mblsize=-blsize barsize=64 ; バーのサイズ barx=240:bary=540 ; バーの座標 barhalf=barsize/2 blhalf=blsize/2 wx=0:wy=80 ; ブロックの表示開始位置(左上) wsx=10:wsy=6 ; ブロックの配置数(X,Y) wpx=x2/wsx:wpy=16 ; ブロック1個あたりのサイズ dim wall,wsx,wsy ; ブロックを表示するフラグ ; 0=表示、1=表示しない score = 0 gameflag = 0 *main redraw 0 color 0,0,128:boxf mtlist touchid ; マルチタッチ mtinfo touch, touchid(0) color 0,255,255:pos 8,8:mes "SCORE: "+score+" / mousex: "+mousex+" / touch: "+touch(0)+", "+touch(1) ;gradf 0,0,x2,y2,1,0,128 ; 画面クリア ; バー : マウス座標から位置を決定 barx=mousex-barsize/2 color 255,255,255 boxf barx,bary,barx+barsize,bary+16 if gameflag { goto *gameover stick key if key&256 { if mousey<300 : goto *gstart } goto *main2 } ; ボール : X方向の移動 blx=blx+bpx if blx<=x1 : blx=x1 : bpx=-bpx if blx>=(x2-blsize) : blx=x2-blsize : bpx=-bpx ; ボール : Y方向の移動 bly=bly+bpy if bly<=y1 : bly=y1 : bpy=45 : bk=1 : blspd=45 if bly>=(y2-blsize) : gameflag = 1 ; ボールとバーがぶつかったかどうか調べます coly=bly+blsize if (coly>=bary)&(bly<(bary+16)) { ; ボールY座標のチェック ; ボールX座標のチェック colx=blx+blhalf x=barx+barhalf if abs(colx-x)<(barhalf+blhalf) { bly=bary-blsize : bpy=-blspd i=(colx-x)/4 if i!=0 : bpx=i bk=1 if clrflag { dim wall,wsx,wsy ; ブロックを復活させる clrflag=0 } } } *main2 ; ブロックの処理 colx=wpx+bsize coly=wpy+bsize left=0 repeat wsy cy=cnt y=cnt*wpy+wy hsvcolor cnt*10,255,255 repeat wsx cx=cnt x=cnt*wpx+wx if wall(cx,cy)=0 { ; ブロックを表示 left++ boxf x,y,x+wpx-2,y+wpy-2 i=blx-x:j=bly-y if (i>=mblsize)&(i<colx)&(j>=mblsize)&(j<coly)&(bk) { wall(cx,cy)=1 bpy=-bpy bk=0 score+=wsy-cy } } loop loop ; ボールを表示 pos blx,bly:color 255,255,255 circle blx,bly,blx+blsize,bly+blsize ;font msgothic,blsize ;mes "●" redraw 1 await 33 if left=0 : clrflag=1 goto *main *gameover color 255,255,255 pos 160,380:mes "GAME OVER" your_name = "" // 既に自分の名前があるか確認 exist "save/myname.txt" if strsize != -1 { notesel your_name noteload "save/myname.txt" noteunsel } // HSP3Dish.js で input は 闇なので独自のインターフェイスを用意した方が良いと思う。。。 objsize 180,32 pos 160,330 : input your_name : id_input_yourName = stat pxy = ginfo_cx, ginfo_cy mes "※ 英数記号のみ" objsize 80,32 pos 360,330 : button "送信", *send // スマホでキーボードを出させる苦肉の策(要素を作成) sdim strbuf, 1024 ; Androidでinput入力を捕捉する必要があるため sdim mae_strbuf, 1024 your_name_ = your_name strrep your_name_, "'", "\\'" ; エスケープ inputSizeX = 180 inputSizeY = 32 inputPosX = 160 inputPosY = 330 exec {" // Androidはinput内容を捕捉する必要がある var mae_hsp_input_value = '"}+your_name_+{"'; if (navigator.userAgent.match(/iPhone|iPad|Android/)) { if(document.getElementById('hsp_dummy') === null){ // キーボード表示用のinput作成(上部に極透明なオブジェクトを作成) const inputElement = document.createElement('input'); inputElement.id = 'hsp_input'; inputElement.type = 'text'; inputElement.inputmode = 'email'; inputElement.style.position = 'fixed'; inputElement.style.left = '0'; inputElement.style.top = '0'; inputElement.style.fontSize = '100px'; inputElement.style.display = 'none'; inputElement.style.zIndex = -1; if (navigator.userAgent.match(/Android/)) { inputElement.value = mae_hsp_input_value; inputElement.style.width = '98%'; inputElement.style.height = '100px'; inputElement.style.opacity = '.2'; }else{ inputElement.style.width = '1px'; inputElement.style.height = '1px'; inputElement.style.opacity = '.01'; } // フォーカスアウトでinputを非表示にして、dummyを表示する inputElement.addEventListener('focusout', (event) => { document.getElementById('hsp_input').style.display = 'none'; document.getElementById('hsp_dummy').style.display = ''; }); // Androidはinput内容を捕捉する必要がある if (navigator.userAgent.match(/Android/)) { inputElement.addEventListener('input', (event) => { if(!event.target.value.match(/^[a-zA-Z0-9!-/:-@\[-`{-~ ]*$/)){ // 半角英数以外は入力禁止(他デバイスに合わせてる) event.target.value = mae_hsp_input_value; } mae_hsp_input_value = event.target.value; stringToUTF8Array(mae_hsp_input_value, Module.HEAP8, "} + varptr(strbuf) + {", 1024); }); } document.body.appendChild(inputElement); // click->focus()のためのダミーElement const dummyElement = document.createElement('div'); dummyElement.id = 'hsp_dummy'; dummyElement.style.position = 'absolute'; dummyElement.style.fontSize = '100px'; SetRectDummyDiv(dummyElement); dummyElement.style.opacity = '.01'; //dummyElement.style.opacity = '.5'; // test用 //dummyElement.style.backgroundColor = 'red'; // test用 dummyElement.addEventListener('click', () => { // スマホはタッチ系のアクション内じゃないとfocus()効かない document.getElementById('hsp_input').style.display = ''; document.getElementById('hsp_dummy').style.display = 'none'; document.getElementById("hsp_input").focus(); }); document.body.appendChild(dummyElement); // ダミーElementのリサイズ function SetRectDummyDiv(erement){ if (erement === null) return; const inputSizeX = "} + inputSizeX + {"; const inputSizeY = "} + inputSizeY + {"; const inputPosX = "} + inputPosX + {"; const inputPosY = "} + inputPosY + {"; const wx = ENV.HSP_WX; // 480 const wy = ENV.HSP_WY; // 800 const sx = ENV.HSP_SX; // String(window.innerWidth) const sy = ENV.HSP_SY; // String(window.innerHeight) const rect = document.getElementById("canvas").getBoundingClientRect(); if( (sx/sy) < (wx/wy) ){ // 横幅に余白がなく縦方向に余白があるとき const rate = wy / wx * sx / sy; // ディスプレイ高さに対してゲーム画面高さがどのくらいか erement.style.left = rect.left + window.pageXOffset + rect.width*(inputPosX/wx)+'px'; erement.style.width = rect.width*(inputSizeX/wx)+'px'; erement.style.top = rect.top + window.pageYOffset + rect.height*((1-rate)/2 + (inputPosY/wy) * rate)+'px'; erement.style.height = rect.height*((inputSizeY/wy) * rate)+'px'; }else { // 縦方向に余白がなく横幅に余白があるとき const rate = wx / wy * sy / sx; // ディスプレイ幅に対してゲーム画面幅がどのくらいか erement.style.left = rect.left + window.pageXOffset + rect.width*((1-rate)/2 + (inputPosX/wx) * rate)+'px'; erement.style.width = rect.width*((inputSizeX/wx) * rate)+'px'; erement.style.top = rect.top + window.pageYOffset + rect.height*(inputPosY/wy)+'px'; erement.style.height = rect.height*(inputSizeY/wy)+'px'; } } // デバイス回転時にダミーElementをリサイズ function DeviceOrientation_InputDummy() { setTimeout(() => { SetRectDummyDiv(document.getElementById('hsp_dummy')); }, 100); } window.removeEventListener('orientationchange', DeviceOrientation_InputDummy); window.addEventListener('orientationchange', DeviceOrientation_InputDummy); }else{ document.getElementById('hsp_dummy').style.display = ''; } } "} objsel id_input_yourName *gameover_loop mtlist touchid ; マルチタッチ if stat { // input部以外タッチでフォーカス外す exec {" if(document.getElementById("hsp_input")){ document.getElementById("hsp_input").blur(); } "} } if mae_strbuf != strbuf { // Androidではinput入力を捕捉する必要があるため mae_strbuf = strbuf your_name = strbuf objprm id_input_yourName, your_name } redraw 0 color 0,0,128:boxf color 0,255,255:pos 8,8:mes "SCORE: "+score color 255,255,255 pos 200,280:mes "GAME OVER" pos 30,335:mes "YOUR NAME : " pos pxy(0),pxy(1) : mes "※ 英数記号のみ" redraw 1 await 33 goto *gameover_loop stop // データ保存 *send clrobj // スマホでキーボードを出させる苦肉の策(要素を削除) exec {" if(document.getElementById('hsp_input')){ document.getElementById('hsp_input').remove(); } if(document.getElementById('hsp_dummy')){ document.getElementById('hsp_dummy').remove(); } "} if your_name == "" : your_name = "NO NAME" // 自分の名前を登録 notesel your_name notesave "save/myname.txt" noteunsel devcontrol "syncfs" // ユニークID生成(テキトウ20桁) exist "save/uniqid.txt" if strsize == -1 { // まだブラウザに保存されていなかったとき uniqId = TekitouUniqId(20) ; 20桁のUniqueID生成 dirlist dummy,"save",5 : if stat == 0 : mkdir "save" ; ← Windowsアプリとして実行するとき用に notesel uniqId notesave "save/uniqid.txt" ; ブラウザに保存 devcontrol "syncfs" noteunsel }else { // すでにブラウザにUniqueIDが保存されていたとき。読み込み notesel uniqId noteload "save/uniqid.txt" noteunsel } // データベースファイル「sample.db」を開き(ファイルが無ければ作られる)、「UserScore」テーブルを作成 (すでにテーブルがあれば作成されません) // - 基本的にデータベースファイルは事前に準備しておきhtmlファイル等と共にアップロードしてください。 ezphpsql "sample.db", "CREATE TABLE IF NOT EXISTS UserScore (ID INTEGER PRIMARY KEY, uniqid TEXT, score INTEGER, name TEXT, UNiQUE(uniqid))" // とりあえずスコア0でユーザーデータを新規挿入。ただしUNiQUE制約があるので既に同じuniqidがある(ゲームを遊んだことがある)場合はErrorが返る(stat=0、がそれは無視してOK) ezphpsql "sample.db", "INSERT INTO UserScore (uniqid, score, name) VALUES (?, ?, ?)", uniqId, 0, your_name ; 第3引数以降はプレースホルダにセットする値で「?」の位置に前から順で割り当てられます // nameは常に更新。scoreは高い時だけ更新。uniqidを目印にして。 query = {" UPDATE UserScore SET name = ?, score = CASE WHEN score < ? THEN ? ELSE score END WHERE uniqid = ?; "} ezphpsql "sample.db", query, your_name, score, score, uniqId ; ezphpsqlは中身が#defineなので第2パラメータまでのときでも直接複数行文字列を置くことはできません。 // score降順ですべてのユーザー分取り出す(単純な平文出力。jsonで出力したい場合はezphpsql_jsonを使ってください。) ezphpsql "sample.db", "SELECT uniqid, score, name FROM UserScore ORDER BY score DESC" rankText = refstr ; シンプルなSELECT文なら平文で受け取って処理すると良いでしょう。(データ内の改行は「<BR>」で置換して出力されます) notesel rankText // ランキング描画 redraw 0 color 0,0,128:boxf color 0,255,255:pos 8,8:mes "SCORE: "+score : mes "" pos 50, maeScore = -1 : maeJunni = 1 repeat notemax / 3 ; 自分が何項目要求したかで何人分返ってきたかを計算。(ここでは uniqid, score, name の3項目要求している) noteget rank_uniqid, cnt*3 : strrep rank_uniqid, "uniqid:", "" ; keyが邪魔なので取り除く noteget rank_score, cnt*3+1 : strrep rank_score, "score:","" : rank_score = int(rank_score) noteget rank_name, cnt*3+2 : strrep rank_name, "name:", "" if rank_uniqid == uniqId : color 255,128,128 : rank_uniqid = " ("+rank_uniqid+")" : else :rank_uniqid = "" : color 255,255,255 ; 自分だけUniqID表示 junni = (cnt+1) : if maeScore == rank_score : junni = maeJunni : else : maeJunni = junni : maeScore = rank_score ; 同スコアは同率順位 mes ""+junni+"位 "+rank_score+" - "+ rank_name + rank_uniqid loop redraw 1 end *owari end // テキトウに N桁 Unique な ID を生成する関数 #module #defcfunc TekitouUniqId int keta randomize uniqId = "" repeat keta uniqid += "_" id = rnd(10+26+26) if id >= 36 : poke uniqId, cnt, id+61 : continue ; a-z if id >= 10 : poke uniqId, cnt, id+55 : continue ; A-Z poke uniqId, cnt, id+48 ; 0-9 loop return uniqId #global