sample\sample_ezphp\
sample06_ezphp-saveload_crc.hsp
#include "hsp3dish.as"
#include "ezlocal-dish-js.hsp"
ezLocalJS
#include "mod_ezphp.hsp"
;
; ブロック崩し(3)
; (ボール・バー・ブロック表示します)
;
; ezphpsave, ezphpload によるテキスト読み書き(+内容変更検出ハッシュ利用) - 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
}
// ランキングテキスト読み込み
notesel rankText
*RETRY_SAVE
ezphpload "save/rank.txt", rankText, crc
if stat == 0 {
// ファイルがまだない
rankText = ""+uniqId+","+score+","+your_name
// ランキングテキスト保存
ezphpsave "save/rank.txt", rankText
}else{
score_ = score
// 自分のデータがあるか確認
rankIndex = notefind(uniqId,1)
if rankIndex != -1 {
// 前回スコアより高いか名前が違えば更新(現在のデータ行を消して再挿入)
noteget temp, rankIndex
split temp, ",", rank_uniqid, rank_score, rank_name
if int(rank_score) < score_ || rank_name != your_name {
notedel rankIndex : rankIndex = -1
if score_ < int(rank_score) : score_ = int(rank_score) ; 前回スコアの方が高いとき
}
}
if rankIndex == -1 {
// ランキングデータ挿入
repeat notemax
noteget temp, cnt
split temp, ",", rank_uniqid, rank_score, rank_name
if int(rank_score) < score_ {
// この行の人より勝ち
noteadd ""+uniqId+","+score_+","+your_name, cnt
rankIndex == 0
break
}
loop
if rankIndex == -1 {
// 最後まで勝てる相手を見つけられなかったときは最下位
noteadd ""+uniqId+","+score_+","+your_name, -1
}
// ランキングテキスト保存
ezphpsave "save/rank.txt", rankText, crc ; 第3パラメータのcrcでsave前にloadした時と内容が変わっていないかチェックできる。もし内容が変わっていたら保存されずstatに-1が代入される
if stat == -1 {
// loadから編集してsaveする間に誰か他のユーザーに先を越されて内容が変わっていた場合
wait 50
goto *RETRY_SAVE ; もう一度ezphploadから保存処理をやり直す
}
}
}
// ランキング描画
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
noteget temp, cnt
split temp, ",", rank_uniqid, rank_score, rank_name
if rank_uniqid == uniqId : color 255,100,100 : 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