2 ****PCD-2013 GameScriptCoreLibrary****
3 Tokyo Gakugei University Senior High School.
6 Mac OSX: Safari, Chrome
7 Windows: Chrome, IE(9 or later)
21 mainManager = new GameManager();
29 var loc = document.location.href;
31 var URL_PCD_Root = loc.slice(0, loc.lastIndexOf("/") + 1);
33 var URL_PCD_Auth = URL_PCD_Root + "auth.php";
34 var URL_PCD_Audio = URL_PCD_Root + "audio/";
35 var URL_PCD_Stage = URL_PCD_Root + "stage/";
37 // ゲームを呼び出す関数 適切なdiv要素で呼び出すとゲームを初期化できる。
39 // audio, corelib, images, stageの各フォルダをコピーしたdirにhtmlを置き、<div>.InitGameManager() と実行するスクリプトを書く
40 // stageName == nullだとステージを開始しない。指定する時は.jsおよびパスを省く
45 var CollideBottom = 4;
49 HTMLDivElement.prototype.InitGameManager = function(stageName)
51 if(this instanceof HTMLDivElement)
53 var man = new GameManager(this, null);
54 if(stageName) man.loadStageFromNetwork.apply(man, [stageName]);
58 throw new TypeError("InitGameManager はdiv要素にしか実行できません");
66 function GameManager(parent, debugTextName){
69 if(debugTextName == undefined) debugTextName = "DebugText";
70 if(parent == undefined) parent = document.getElementById("MainArea");
73 if(parent.style.position != 'absolute') parent.style.position = 'relative';
77 this.networkManager = new NetworkManager(this);
78 this.UIManager = new UIManager(this);
79 this.userManager = new UserManager(this);
80 this.mainArea = parent;
81 //必要最低限のCanvasとコンテキストの設定
82 this.mainCanvas = createCanvas("MainCanvas", 640, 480, 0, 0, 1, parent);
83 this.mainCanvas.style.border = "solid 1px";
84 this.mainContext = this.mainCanvas.getContext('2d');
85 this.debugText = document.getElementById(debugTextName); //要素が存在しないとnullになり、デバッグが無効になる
86 if(!this.debugText) this.debugText = null;
90 if(!this.isAvailableBrowser()){
94 this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
95 this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
96 this.mainContext.font = "normal 20px sans-serif";
97 //実行中のGameStageオブジェクトを格納
98 this.runningStage = null;
99 this.runningStageName = null;
100 //現在存在しているWidghetのリストを格納
101 this.runningWidgets = [];
105 // pauseStage()関連の配列。
106 this.stagePaused = false;
107 this.stagePausedFunctions = [];
108 this.stagePausedInitFunctions = [];
110 this.backgroundMusic = null;
113 //コールバックを行うために、イベントリスナーのmanagerプロパティにGameManagerのインスタンスを代入する。
115 timerTickEventListener.manager = this;
116 window.setInterval(timerTickEventListener, 1000/this.tickPerSecond);
118 //各種コールバック(使用元のスクリプトで使う用)
119 this.stageStartedEvent = null; //ステージが開始されたときに呼ばれる。引数: stage
120 this.stageStoppedEvent = null; //ステージが終了されたときに呼ばれる。引数: stage
122 GameManager.prototype = {
123 //タイマーカウントの秒あたりの回数を設定
125 timerTick: function(){
126 //各オブジェクトの単位時間ごとの動作と再描画を行う
129 if(!this.stagePaused){
131 if(this.runningStage){
133 this.runningStage.timerTick();
136 // runningStage.timerTick() 内でpauseStage()された時、ここで再度判定しないとWidghetのtickが実行されてしまう
137 if(!this.stagePaused){
139 for(var i = 0; i < this.runningWidgets.length; i++){
140 var w = this.runningWidgets[i];
142 // Widghetのtick()からfalseで帰ってきたらWidghetを開放
143 this.removeWidget(w);
149 this.stagePausedFunctions[0]();
154 if(this.runningStage){
156 this.runningStage.draw();
159 for(var i = 0; i < this.runningWidgets.length; i++){
160 var w = this.runningWidgets[i];
164 //各オブジェクトの走査が終わってから、死亡判定およびステージ再読み込みを行う。
165 if(this.runningStage && this.runningStage.userControlledCharacter){
166 if(this.runningStage.userControlledCharacter.HP == 0){
167 this.runningStage.userControlledCharacter.HP = this.runningStage.userControlledCharacter.max_HP;
168 this.addWidget(new MessageWidgetClass(this, ["死んでしまった……\n", null, function(w){
169 w.manager.UIManager.clearInput();
170 w.manager.runningStage.userControlledCharacter.HP = w.manager.runningStage.userControlledCharacter.max_HP;
171 if(w.manager.runningStageName){
172 w.manager.loadStageFromNetwork(w.manager.runningStageName);
174 //ローカルモード時は動作を停止させるだけ
175 w.manager.stopStage();
182 addWidget: function(w){
184 this.runningWidgets.push(w);
186 removeWidget: function(w){
187 if(removeObjectFromArray(this.runningWidgets, w))
192 runStage: function(stage){
194 //実行中のステージがあれば終了処理を行わせる。
195 if(this.runningStage){
199 //GameManager側の情報をGameStageに渡す。
200 stage.manager = this;
201 stage.mainCanvas = this.mainCanvas
202 stage.debugCanvas = this.debugCanvas
203 stage.mainContext = this.mainContext
204 stage.debugContext = this.debugContext
205 //GameStage側の初期化処理を行わせる。
208 this.networkManager.joinStage(stage);
209 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
210 this.runningStage = stage;
212 if(this.stageStartedEvent)
214 this.stageStartedEvent.apply(window, [stage]);
218 // func : 停止中にtick毎に呼ばれる関数。
219 // initFunc : pauseが可能となった時に呼ばれる関数。指定しないと、その時点でpauseが不可能だったときに例外がおこる。
220 pauseStage: function(func, initFunc){
221 //ステージの実行を一時停止する。一時停止中、funcに指定された関数が毎tick毎に呼ばれる
222 if(!this.stagePaused){
230 this.stagePausedFunctions.push(func);
231 this.stagePausedInitFunctions.push(initFunc);
232 this.stagePaused = true;
237 throw "pauseStage calling doubled.";
240 this.stagePausedFunctions.push(func);
241 this.stagePausedInitFunctions.push(initFunc);
246 //func は、pauseStageのinitFuncが実行されてそのpauseStage()による待機状態に入る前にresumeStage()が実行された時の安全策
247 resumeStage: function(func){
249 if(this.stagePaused) {
250 //必ずpauseStage()の引数に指定したfunc()の中から呼ばれる・・・はず。
251 var beforeFunc = this.stagePausedFunctions[0];
255 for(var i in this.stagePausedFunctions)
257 if(this.stagePausedFunctions[i] == func)
267 this.stagePausedFunctions.splice(ind, 1);
268 this.stagePausedInitFunctions.splice(ind, 1);
272 this.stagePausedFunctions.splice(0, 1);
273 this.stagePausedInitFunctions.splice(0, 1);
275 if(this.stagePausedFunctions.length == 0)
277 this.stagePaused = false;
280 if(this.stagePausedFunctions[0] != beforeFunc) this.stagePausedInitFunctions[0]();
287 stopStage: function(){
289 if(this.runningStage){
290 //runningStageから設定解除することで、イベントの通知は行われなくなる。
291 var aGameStage = this.runningStage;
292 this.runningStage = null;
293 //GameStage側の終了処理を行わせる。
294 aGameStage.stopStage();
295 //GameStageインスタンスからGameManagerの情報を削除する。
296 aGameStage.manager = null;
297 aGameStage.mainCanvas = null;
298 aGameStage.debugCanvas = null;
299 aGameStage.mainContext = null;
300 aGameStage.debugContext = null;
301 this.stagePausedFunctions = [];
302 this.stagePausedInitFunctions = [];
303 this.stagePaused = false;
305 //画面上に表示されたすべてのWidgetを解放する
306 for(;this.runningWidgets.length>0;)
308 this.removeWidget(this.runningWidgets[0]);
311 if(this.stageStoppedEvent)
313 this.stageStoppedEvent.apply(window, [aGameStage]);
317 loadStageFromLocal: function(code){
320 URL_PCD_Auth = URL_PCD_Root + "auth.php";
321 URL_PCD_Audio = URL_PCD_Root + "audio/";
322 URL_PCD_Stage = URL_PCD_Root + "stage/";
324 var stage = eval(code);
325 mainManager.runStage(stage);
327 loadStageFromNetwork: function(name){
328 //URL_PCD_Stage/name.jsを利用してステージを作成する。
329 var request = this.networkManager.CreateRequestObject();
331 request.open('GET', URL_PCD_Stage + name + ".js", false);
332 this.networkManager.RequestObjectDisableCache(request);
335 if(request.status == 0){
336 alert("ネットワークにアクセスできません。" + request.status + ":" + request.statusText);
337 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
338 if(this.userID != 0){
339 var rq2 = this.networkManager.CreateRequestObject();
341 rq2.open('GET', URL_PCD_Auth + "?action=chstg&name=" + name + "&id=" + this.userID);
342 this.networkManager.RequestObjectDisableCache(rq2);
345 var stage = eval(request.responseText);
346 this.runStage(stage);
347 this.runningStageName = name;
349 alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
352 debugOut: function(str){
353 if(this.debugText != null)
358 this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
361 this.debugText.innerHTML = str + this.debugText.value;
365 isAvailableBrowser: function(){
366 //ブラウザの判別を行う。実行不可能な場合はfalseを返す。
367 //http://d.hatena.ne.jp/Naotsugu/20110927/1317140891
368 var userAgent = window.navigator.userAgent.toLowerCase();
369 var appVersion = window.navigator.appVersion.toLowerCase();
371 if (userAgent.indexOf('opera') != -1) {
373 this.debugOut("Browser:Opera\n");
374 } else if (userAgent.indexOf('msie') != -1) {
375 if (appVersion.indexOf("msie 9.") != -1) {
377 this.debugOut("Browser:IE9\n");
378 } else if (appVersion.indexOf("msie 8.") != -1) {
380 this.debugOut("Browser:IE8\n");
381 } else if (appVersion.indexOf("msie 7.") != -1) {
383 this.debugOut("Browser:IE7\n");
384 } else if (appVersion.indexOf("msie 6.") != -1) {
386 this.debugOut("Browser:IE6\n");
388 this.debugOut("Browser:IE?\n");
391 } else if (userAgent.indexOf('chrome') != -1) {
393 this.debugOut("Browser:Chrome\n");
394 } else if (userAgent.indexOf('safari') != -1) {
396 this.debugOut("Browser:Safari\n");
397 } else if (userAgent.indexOf('gecko') != -1) {
399 this.debugOut("Browser:Gecko\n");
402 this.debugOut("Browser:Unknown\n");
404 //描画コンテキストからHTML5対応チェック
405 if(!this.mainCanvas || !this.mainCanvas.getContext){
407 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザ(Google Chrome等)でアクセスしてください...。");
412 setBackgroundMusic: function(name){
413 if(this.backgroundMusic){
415 this.backgroundMusic.pause();
416 this.backgroundMusic = null;
419 //引数がnullでなければaudioオブジェクトを取得
420 this.backgroundMusic = createAudio(name);
421 if(this.backgroundMusic){
423 this.backgroundMusic.loop = true;
424 this.backgroundMusic.play();
433 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトまたはwindowになり、通常とは異なるので注意。
435 function timerTickEventListener(event)
437 timerTickEventListener.manager.timerTick(event);