質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

enchant.js

enchant.jsとは、アプリやゲームを簡単に開発できるオープンソースのHTML5+JavaScriptベースのフレームワークです。プログラミング学習にも用いられ、多くの素材やプラグインが用意されています。

Q&A

解決済

2回答

968閲覧

cookieに大量のデータを保存し、取得をしたい

l_h_l_h

総合スコア22

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

enchant.js

enchant.jsとは、アプリやゲームを簡単に開発できるオープンソースのHTML5+JavaScriptベースのフレームワークです。プログラミング学習にも用いられ、多くの素材やプラグインが用意されています。

0グッド

0クリップ

投稿2018/09/08 06:33

編集2018/09/08 06:35

JavaScriptのenchant.jsというフレームワークでノベルゲームを制作しておりますが、セーブ機能の実装がイマイチ上手くできません。

●実現したいこと
・セーブ・ロードが行える
・シーン、テキスト再開位置、主人公のフラグや得点を記録する
・得点を得られるシーンをロードし直しても得点が増えない(1つのデータで同じ処理を繰り返さない)
・1人のユーザーで複数のデータを保存できる(とりあえず今回は3つ)

ゲームの基礎的な部分はこちらで紹介されているエンジンを使っています。
game.jsが今回主に対象となるファイルです。

document.cookie = ●●
として縦に長々と書いていけば当機能は実現できると思われますが、それでは長くなりすぎるので、何とか上手く構造化したいと思っています。

今回、セーブ機能を実装するための案として、SaveData.jsを作りSaveDataクラス内のコンストラクタにデータを保存し、連想配列として取り出していくことなどを考えましたが、cookieを使う場合、このようにファイルを分けてしまうと自分の能力では意味がないように思います。
もしcookie以外の他の機能を使った方が良いということであればやり方を変えてみようと思います。
現在はsaveScene(シーン),saveData(テキストの再開位置)だけをcookieに保存し、取得することが可能となった状態です。

以下、ソースコードです。(文字数制限にかかるため一部省略しています)
今回私が弄った部分は200行目あたりです。

enchant(); var saveScene; var saveData; var loadFlag = 0; class CurrentScene{ //コンストラクタ constructor(){ //特に処理なしにしています } ・ ・ ・ //テキストをシーンに設定する SetText(args){ //フラグ用使いまわします。 let i = 1; if(loadFlag == 1){ i = saveData; loadFlag = 0; }   //引数例 ["ねね","あっ!進!","おはよう","今日は転校生が来るんだよね!","噂だと女の子なんだって!"],   //iは初期値1なのは、発言者だからです。 //発言者がいない場合は無視する if(args[0] != ""){ let whosay = new Label("「" + args[0] + "」"); whosay.font = "16px monospace"; whosay.color = "rgb(0, 0, 0)"; whosay.y = 180; whosay.x = 10; whosay.width = 300; whosay.height = 120; whosay.opacity = 0; whosay.tl.fadeIn(15); textLayer.addChild(whosay); } //発言内容を表示する let text = new Label( args[i] ); text.font = "16px monospace"; text.color = "rgb(0, 0, 0)"; text.y = 200; text.x = 10; text.width = 300; text.height = 120; text.opacity = 0; text.tl.fadeIn(15); textLayer.addChild(text); //次へボタン let nextlabel = new Label("< ▼ >"); nextlabel.font = "16px monospace"; nextlabel.color = "rgb(0, 0, 0)"; nextlabel.y = 280; nextlabel.x = 270; nextlabel.opacity = 0; nextlabel.tl.fadeIn(20); textLayer.addChild(nextlabel); //戻るボタン let backLabel = new Label("< ▲ >"); backLabel.font = "16px monospace"; backLabel.color = "rgb(0, 0, 0)"; backLabel.y = 280; backLabel.x = 0; backLabel.opacity = 0; backLabel.tl.fadeIn(20); textLayer.addChild(backLabel); //セーブボタン let saveLabel = new Label("セーブ"); saveLabel.font = "16px monospace"; saveLabel.color = "rgb(0, 0, 0)"; saveLabel.y = 280; saveLabel.x = 130; saveLabel.opacity = 0; saveLabel.tl.fadeIn(20); textLayer.addChild(saveLabel); let saveFinish = new Label("セーブが完了しました"); saveFinish.font = "16px monospace"; saveFinish.color = "rgb(0, 0, 0)"; saveFinish.y = 140; saveFinish.x = 120; saveFinish.opacity = 0; saveFinish.tl.fadeIn(20); let saveOK = new Label("OK"); saveOK.font = "16px monospace"; saveOK.color = "rgb(0, 0, 0)"; saveOK.y = 160; saveOK.x = 120; saveOK.opacity = 0; saveOK.tl.fadeIn(20); let saveBG = new Sprite(320,180); saveBG.image = core.assets["./img/serifu.png"]; saveBG.x = 50; saveBG.y = 90; saveBG.scaleY = 0.5; let loadLabel = new Label("ロード"); loadLabel.font = "16px monospace"; loadLabel.color = "rgb(0, 0, 0)"; loadLabel.y = 280; loadLabel.x = 190; loadLabel.opacity = 0; loadLabel.tl.fadeIn(20); textLayer.addChild(loadLabel); //次へボタン //配列に要素があればラベルを書き換える nextlabel.addEventListener('touchstart',function(e){ if(i != args.length -1){ i++; text.text = args[i]; }else{ //セリフの配列が空になった時に次のシーンへ飛ばす executeNext(eval(getNextSceneName()),1); } }); //戻るボタン backLabel.addEventListener('touchstart',function(e){ if(i != 1){ i--; text.text = args[i]; }else{ //処理なし } }); saveLabel.addEventListener('touchstart',function(e){ saveScene = getCurrentSceneName(); textLayer.addChild(saveFinish); textLayer.addChild(saveOK); bgLayer.addChild(saveBG); saveData = i; document.cookie = 'saveScene=' + encodeURIComponent(saveScene); document.cookie = 'saveData=' + encodeURIComponent(saveData); console.log(saveData); console.log(i); console.log(document.cookie); }); saveOK.addEventListener('touchstart',function(e){ textLayer.removeChild(saveFinish); textLayer.removeChild(saveOK); bgLayer.removeChild(saveBG); }); if(!document.cookie){ loadLabel.color = "#ccc"; }else{ loadLabel.addEventListener('touchstart',function(e){ loadFlag = 1; console.log(document.cookie.split(';')[0].split('=')[1]); console.log(document.cookie.split('=')[2]); saveScene = document.cookie.split(';')[0].split('=')[1] saveData = document.cookie.split('=')[2]; executeNext(eval(saveScene),saveData); }); } }//end SetText SetCurrentSceneName(name){ this.currentSceneName = name; } SetNextSceneName(name){ this.nextSceneName = name; } //次のシーンへ遷移する GoNextScene(args){ //テキストと次へボタンを削除する textLayer.removeChild(textLayer.firstChild); textLayer.removeChild(textLayer.firstChild); textLayer.removeChild(textLayer.lastChild); textLayer.removeChild(textLayer.lastChild); textLayer.removeChild(textLayer.firstChild); textLayer.removeChild(textLayer.firstChild); textLayer.removeChild(textLayer.firstChild); textLayer.removeChild(textLayer.lastChild); textLayer.removeChild(textLayer.lastChild); textLayer.removeChild(textLayer.firstChild); //ここで関数名と引数のセットを持ってきています for(let val in args){ _currentScene[val](args[val]); } } ・ ・ ・ // シナリオを実行する関数 function executeNext(args , saveData) { _currentScene.GoNextScene(args , saveData); } function getNextSceneName(){ return _currentScene.nextSceneName; } function getCurrentSceneName(){ return _currentScene.currentSceneName; } ////////////////////////////////////// // 各種設定等 ////////////////////////////////////// imglist = ["./img/serifu.png","./img/material.png"] //bgmlist = ["やばいシーン.mp3","家.mp3","ホップ.mp3","明るい街.mp3","バッドエンド.mp3","可愛い.mp3","ノーマルエンドに最適.mp3","op.mp3"] // シーンを生成する let _currentScene = new CurrentScene(); //プレイヤーを作成する //var player = new MainPlayer(); ////////////////////////////////////// // 初期化処理 ////////////////////////////////////// window.onload = function() { core = new Core(640, 320); core.fps = 16; //使用する画像をプリロードする(配列を渡せばOK) core.preload(imglist); //core.preload(bgmlist); // core.bgm = Sound.load('op.mp3'); //ここで初期化処理が始まる core.onload = function() { // core.bgm.volume = 0.3; // core.bgm.play(); //core.bgm.loop = true; //レイヤーで管理する bgLayer = new Group(); core.rootScene.addChild(bgLayer); imageLayer = new Group(); core.rootScene.addChild(imageLayer); textLayer = new Group(); core.rootScene.addChild(textLayer); /*これが最初のシーンです */ _currentScene.SetBackGroundImage("./img/serifu.png"); //引数リスト //選択肢1 遷移先1 選択肢2 遷移先2 //最初は選択肢のあるシーンを作成しています。 _currentScene.SetChoiceScene(["初めから",TitleScene,"あはん",TitleScene]); let loadLabel = new Label("続きから"); loadLabel.font = "16px monospace"; loadLabel.color = "rgb(0, 0, 0)"; loadLabel.y = 280; loadLabel.x = 10; textLayer.addChild(loadLabel); if(!document.cookie){ loadLabel.color = "#ccc"; }else{ loadLabel.addEventListener('touchstart',function(e){ loadFlag = 1; console.log(document.cookie.split(';')[0].split('=')[1]); console.log(document.cookie.split('=')[2]); saveScene = document.cookie.split(';')[0].split('=')[1] saveData = document.cookie.split('=')[2]; executeNext(eval(saveScene),saveData); }); } } core.start(); }

どなたかご助言いただけないでしょうか
よろしくお願いいたします

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

l_h_l_h

2018/09/08 08:23

ありがとうございます。すみません、書き方が悪かったです。大量のデータと言うのは、変数の量のことで、容量そのもののことではありません。変数は各々数字や文字列が格納されているだけですので、容量的にはそんなに大きくならないと思います。
guest

回答2

0

ベストアンサー

ブラウザ側で保存したいのであれば
LocalStorageはいかがでしょうか?

投稿2018/09/08 07:45

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

l_h_l_h

2018/09/08 08:32

回答ありがとうございます。 少し調べてみましたがcookieを使うよりは良さそうですね ただ保存や取得の方法がcookieとあまり変わらないように思えるのですが、 ・セーブデータをセーブデータ用のファイルで管理できる ・連想配列などを用いて効率的なコードが書ける ということはできるのでしょうか これらは必須条件ではありませんが、管理しやすく読みやすいコードを書きたいです(例えば、セーブしてロードする機能だけで何百行も書いていると管理しにくいです) 一般的な話をお聞かせいただければ後はコチラで調査し実装してみようと思います お手数おかけしますがよろしくお願いします
退会済みユーザー

退会済みユーザー

2018/09/08 08:46

保存時にセーブデータ毎に 'SaveData1' などとキーを指定してあげれば 別々のセーブデータとして残すことができます 例) local.setItem('saveData1', 'ここにセーブデータをいれる'); 注意点として、文字列で保存されるので、Object型などで書いている場合文字列へ・文字列からの変換が必要になります。 (そこは JSON.parse() や JSON.stringify() があるので大丈夫でしょう) また、cookieは数KBくらいですが LocalStorageは数MB~10MB くらいだったと思います
退会済みユーザー

退会済みユーザー

2018/09/08 08:48

なので、 > ・セーブデータをセーブデータ用のファイルで管理できる これははい、で > ・連想配列などを用いて効率的なコードが書ける は、文字列との相互変換を行えば可能です
l_h_l_h

2018/09/08 09:19

ありがとうございます いけそうな気がしてきたので一度試してみたいと思います
l_h_l_h

2018/09/08 14:10

すみません セーブデータ用のファイルで管理というのは例えばSaveData.jsを作り、その中のコンストラクター等で 'saveScene':saveScene 'saveData':saveData という感じに管理し、データの保存、取得の時はSaveData.jsからデータを呼び出したいということです しかしこの方法ではsaveData.jsに格納されるデータをローカルストレージに保存する場合、結局saveData.jsは不要(冗長になるだけ)なのではないか、と思います ローカルストレージを使えば他ファイルにデータを保存しつつ呼び出すこともできる方法があるということで良いんでしょうか それとも諦めてメインファイルにコードを書くべきでしょうか(こちらなら各機能をメソッド化してできそうな気がしますが、結局メインファイルの文量が多くなりややこしくなりそうです)
退会済みユーザー

退会済みユーザー

2018/09/08 15:08

他の言語のクラス的な考え方で、SaveData.jsにセーブデータの連想配列と取得・保存の処理を作って game.jsから呼び出す、ではいかがでしょうか 「他ファイルに保存」というのが気になったのですが、 JavaScriptの場合はファイルごとに名前空間が別れているわけではないです。 SaveData.jsに書いたからといってgame.jsから呼び出せなくなるわけではありません SaveData.jsに var savedata = {}; などと定義したものは game.jsからも呼び出せます 最終的には、質問者様のやりやすい方法でどうぞ・・・
l_h_l_h

2018/09/14 07:45

御返事遅れました 色々と実装を試してみたのですが、現在MainPlayer.jsにて主人公のステータスを管理し、そこのステータスに現在のシーンとテキストの位置を保持させることにしました class MainPlayer{ constructor(){ this.status={ 'saveScene':null, 'saveData':0, 'pointA':0, 'pointB':0, }; } } また、セーブボタンが押された時にsaveData.js内のメソッドを呼び出し function setSaveData(args){ player.status[args]= getCurrentSceneName(); console.log("a="+player.status['saveScene']); } とすることで、MainPlaer.js内の値を書き換えることに成功しました(game.js内でsetSaveData('saveScene')を使います) しかし肝心のローカルストレージの使い方が分からず、左辺を localStorage.player.status['saveScene']= とするとundifinedとなりエラーとなります(localStorageの位置を変えても同じ結果でした) 連想配列内の変数にはlocalStorageは適用できないのでしょうか 個人的にはこれでいけると思った方法だったのですが…… 数日かかってしまって申し訳ございません お手数ですが、何か分かればご助言いただきたいです
l_h_l_h

2018/09/14 15:14

すみません少し変更しました function setSaveData(args){ player.status[args]= getCurrentSceneName(); console.log("a="+player.status['saveScene']); setLocal(); } function setLocal(){ localStorage.setItem('json',JSON.stringify(player.status)); var data = localStorage.getItem('json'); data = JSON.parse(data); console.log("b=" + data['saveScene'][0]); console.log("c=" + data['saveScene'].length); for (var i = 0; i < data['saveScene'].length; i++) { console.log(data['saveScene'][i]); } } このように変更しました(参考:http://itemy.net/?p=1427) 調べたところこれでうまくいくはずなのですが、出力は1文字ずつとなってしまいました もし何か分かればご助言いただきたいのですが、日数も経っていますので、対応できなければその旨だけお伝えいただければと思います よろしくお願い申し上げます
退会済みユーザー

退会済みユーザー

2018/09/14 15:27

getCurrentSceneName(); これが文字列を返すのであれば、player.status[args]には文字列がはいりますから 仮にsetSaveData(args)に 'saveScene' を渡すと player.status.saveScene は文字列になります。 するとsetLocal()内でdataにはplayer.statusが入っていますから data.saveSceneは文字列になり、それをfor文で回しているから1文字ずつでているということになりますね
l_h_l_h

2018/09/14 16:23

ありがとうございます setLocal内に以下の文を入れることでデータを取り出すことに成功しました var da=""; for (var i = 0; i < data['saveScene'].length; i++) { da += data['saveScene'][i]; console.log("da="+da); console.log(data['saveScene'][i]); } ひとまず目標達成です ありがとうございました
guest

0

jQueryを使用できる環境であれば、jquery.cookie.jsというjQueryプラグインがあります。https://github.com/carhartl/jquery-cookieからダウンロードできます。

Cookieに格納する文字列を自分で構築したり分割したりするのではなく、オブジェクトのように扱いたい(すなわちkey: value形式でデータを保存したり読み出したりしたい)ということでしたらこのプラグインが有用だと思います。

投稿2018/09/08 09:02

carrotRakko

総合スコア77

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

l_h_l_h

2018/09/08 09:21

回答ありがとうございます 今回の事例でkey:value形式で取り出すのがなかなかうまくいかなかったので、助かります 一度試してみたいと思います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問