OSDN Git Service

隠しブロック機能を追加。メインキャラクターの当たり判定範囲も変更
[h58pcdgame/GameScriptCoreLibrary.git] / www / corelib / core.js
1 /*
2 ****PCD-2013 GameScriptCoreLibrary****
3 Tokyo Gakugei University Senior High School.
4
5 <クライアント要件>
6 Mac OSX: Safari, Chrome
7 Windows: Chrome, IE(9 or later)
8
9 以下の技術が実行可能である必要があります:
10 HTML5
11         Canvas
12         FormData
13
14 <Usage>
15 HTMLソース側に、
16 <div id="MainArea">
17 </div>
18 <div id="Resources">
19 </div>
20 および
21 mainManager = new GameManager();
22 が必要。
23 */
24
25 //
26 //定数
27 //
28
29 var URL_PCD_Root = "http://localhost/pcd2013dev/www/";
30 //var URL_PCD_Root = "http://192.168.6.242/pcd2013dev/www/";
31 //var URL_PCD_Root = "http://192.168.0.3/PCD2013GSCL/www/";
32 //var URL_PCD_Root = "http://192.168.6.242/pcd2013hikarupsp/pcd2013dev/www/";
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/";
36
37 //
38 //ゲームマネージャー
39 //
40
41 function GameManager(){
42         //ユーザーID
43         this.userID = 0;
44         //ネットワークマネージャーの設定
45         this.networkManager = new NetworkManager();
46         //必要最低限のCanvasとコンテキストの設定
47         this.mainCanvas = createCanvas("MainCanvas", 640, 480, 0, 0, 1, "MainArea");
48         this.mainCanvas.style.border = "solid 1px";
49         this.mainContext = this.mainCanvas.getContext('2d');
50         this.debugText = document.getElementById('DebugText');
51         //ブラウザチェック
52         this.isIE = false;
53         if(!this.isAvailableBrowser()){
54                 return null;
55         }
56         //描画コンテキストの初期設定
57         this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
58         this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
59         this.mainContext.font = "normal 20px sans-serif";
60         //実行中のGameStageオブジェクトを格納
61         this.runningStage = null;
62         //現在存在しているWidghetのリストを格納
63         this.runningWidghets = [];
64         //タイマーカウントを初期化
65         this.tickCount = 0;
66         this.timeStamp = 0;
67         //タイマーカウントの秒あたりの回数を設定
68         this.tickPerSecond = 60;
69         this.updateIntervalMs = 128;
70         //キーボード状態を格納するプロパティの設定
71         this.keyState = new Object();
72         this.keyState.upArrow = false;
73         this.keyState.downArrow = false;
74         this.keyState.leftArrow = false;
75         this.keyState.rightArrow = false;
76         // pauseStage()が呼ばれたときにnullじゃなくなる
77         this.stagePausedFunction = null;
78         
79         this.backgroundMusic = null;
80         
81         //**イベントリスナー設定**
82         //コールバックを行うために、イベントリスナーのmanagerプロパティにGameManagerのインスタンスを代入する。
83         //keyDown
84         keyDownEventListener.manager = this;
85         window.addEventListener('keydown', keyDownEventListener, true);
86         //keyUp
87         keyUpEventListener.manager = this;
88         window.addEventListener('keyup', keyUpEventListener, true);
89         //timerTick
90         timerTickEventListener.manager = this;
91         window.setInterval(timerTickEventListener, 1000/this.tickPerSecond);
92         timeStampTimerTickEventListener.manager = this;
93         window.setInterval(timeStampTimerTickEventListener, this.updateIntervalMs);
94 }
95 GameManager.prototype = {
96         keyDown: function(event){
97                 //コールバックではなくコールバック関数(keyDownEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
98                 keyCode = event.keyCode;
99                 switch(event.keyCode){
100                         case 38:
101                                 //上カーソル
102                                 this.keyState.upArrow = true;
103                                 break;
104                         case 40:
105                                 //下カーソル
106                                 this.keyState.downArrow = true;
107                                 break;
108                         case 37:
109                                 //左カーソル
110                                 this.keyState.leftArrow = true;
111                                 break;
112                         case 39:
113                                 //右カーソル
114                                 this.keyState.rightArrow = true;
115                                 break;
116                 }
117                 //実行中のステージに通知
118                 if(this.runningStage){
119                         this.runningStage.keyDown(event);
120                         switch(event.keyCode){
121                                 case 38:
122                                 case 40:
123                                 case 37:
124                                 case 39:
125                                         event.preventDefault();
126                                         break;
127                         }
128                 }
129         },
130         keyUp: function(event){
131                 //コールバックではなくコールバック関数(keyUpEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
132                 keyCode = event.keyCode;
133                 switch(event.keyCode){
134                         case 38:
135                                 //上カーソル
136                                 this.keyState.upArrow = false;
137                                 break;
138                         case 40:
139                                 //下カーソル
140                                 this.keyState.downArrow = false;
141                                 break;
142                         case 37:
143                                 //左カーソル
144                                 this.keyState.leftArrow = false;
145                                 break;
146                         case 39:
147                                 //右カーソル
148                                 this.keyState.rightArrow = false;
149                                 break;
150                 }
151                 //実行中のステージに通知
152                 if(this.runningStage){
153                         this.runningStage.keyUp(event);
154                 }
155         },
156         timerTick: function(){
157                 //各オブジェクトの単位時間ごとの動作と再描画を行う
158                 //単位時間ごとの動作
159                 this.tickCount++;
160                 if(this.stagePausedFunction == null){
161                         //ポーズしていなければ更新処理
162                         if(this.runningStage){
163                                 //ステージ
164                                 this.runningStage.timerTick();
165                         }
166                         //ウィジェット
167                         for(var i = 0; i < this.runningWidghets.length; i++){
168                                 var w = this.runningWidghets[i];
169                                 if(!w.tick()){
170                                         // Widghetのtick()からfalseで帰ってきたらWidghetを開放
171                                         this.runningWidghets.splice(i, 1);
172                                         i--;
173                                 }
174                         }
175                 } else{
176                         //ポーズしているならば処理関数を実行
177                         this.stagePausedFunction();
178                 }
179                 
180                 //描画処理
181                 if(this.runningStage){
182                         //ステージ
183                         this.runningStage.draw();
184                 }
185                 //ウィジェット
186                 for(var i = 0; i < this.runningWidghets.length; i++){
187                         var w = this.runningWidghets[i];
188                         w.draw();
189                 }
190         },
191         timeStampTimerTick: function(){
192                 //ネットワーク同期を司る
193                 //サーバーとの同期カウンタ・タイマー
194                 this.timeStamp += this.updateIntervalMs;
195                 //サーバーに更新情報をアップロード
196                 if(this.userID != 0){
197                         //ユーザーIDが設定されている=オンライン状態
198                         request = this.networkManager.CreateRequestObject();
199                         //同期モード
200                         request.open('POST', URL_PCD_Root + "update.php?uid=" + this.userID + "&action=refresh", false);
201                         request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
202                         //送信データを準備
203                         aData = new RequestData();
204                         for(i = 0; i < this.runningStage.globalStageObjectList.length; i++){
205                                 anObj = this.runningStage.globalStageObjectList[i];
206                                 if(anObj.ownerUID == this.userID){
207                                         aData.append(i, anObj.objectID + "|" + anObj.origin.x + "|" + anObj.origin.y + "|" + anObj.movingSpeed.x + "|" +  anObj.movingSpeed.y+ "|" + anObj.attribute);
208                                 }
209                         }
210                         //送信
211                         this.networkManager.RequestObjectDisableCache(request);
212                         request.send(aData.data);
213                         //レスポンス確認
214                         if(request.status == 0){
215                                 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
216                         }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
217                                 var res = request.responseText;
218                                 if(isValidResponseText(res)){
219                                         var retArray = eval(res);
220                                         this.timeStamp = retArray[0];
221                                         //削除処理
222                                         for(var i = 0; i < retArray[3].length; i++){
223                                                 this.debugOut("Network:removeObject:ObjectID=" + retArray[3][i] + "\n");
224                                                 var anObject = this.runningStage.getGlobalStageObject(retArray[3][i]);
225                                                 if(anObject){
226                                                         this.runningStage.removeStageObject(anObject);
227                                                 }
228                                         }
229                                         //更新処理
230                                         for(var i = 0; i < retArray[1].length; i++){
231                                                 //this.debugOut("Network:refreshObject:ObjectID=" + retArray[1][i] + "\n");
232                                                 anArray = retArray[1][i];
233                                                 var anObject = this.runningStage.getGlobalStageObject(anArray[0]);
234                                                 if(anObject){
235                                                         anObject.origin.x = anArray[1];
236                                                         anObject.origin.y = anArray[2];
237                                                         anObject.movingSpeed.x = anArray[3];
238                                                         anObject.movingSpeed.y = anArray[4];
239                                                 }
240                                         }
241                                         //追加処理
242                                         for(var i = 0; i < retArray[2].length; i++){
243                                                 anArray = retArray[2][i];
244                                                 var args = eval(anArray[7]);
245                                                 var anObject = eval("new " + anArray[5] + "(this.runningStage, args);");
246                                                 if(anObject){
247                                                         this.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
248                                                         anObject.objectID = anArray[0];
249                                                         anObject.origin.x = anArray[1];
250                                                         anObject.origin.y = anArray[2];
251                                                         anObject.movingSpeed.x = anArray[3];
252                                                         anObject.movingSpeed.y = anArray[4];
253                                                         this.runningStage.addStageObject(anObject, true);
254                                                 }
255                                         }
256                                 }
257                         }else{
258                                 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
259                         }
260                         
261                 }
262         },
263         addWidghet: function(w){
264                 this.runningWidghets.push(w);
265         },
266         runStage: function(stage){
267                 //新たなステージを開始する
268                 //実行中のステージがあれば終了処理を行わせる。
269                 if(this.runningStage){
270                         this.stopStage();
271                 }
272                 //新たに開始するステージの初期化
273                 //GameManager側の情報をGameStageに渡す。
274                 stage.manager = this;
275                 stage.mainCanvas = this.mainCanvas
276                 stage.debugCanvas = this.debugCanvas
277                 stage.mainContext = this.mainContext
278                 stage.debugContext = this.debugContext
279                 //GameStage側の初期化処理を行わせる。
280                 stage.runStage();
281                 //サーバーに追加オブジェクトの情報をアップロード
282                 if(this.userID != 0){
283                         request = this.networkManager.CreateRequestObject();
284                         //同期モード
285                         request.open('POST', URL_PCD_Root + "update.php?uid=" + this.userID + "&action=add", false);
286                         request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
287                         //送信データを準備
288                         aData = new RequestData();
289                         for(i = 0; i < stage.globalStageObjectList.length; i++){
290                                 anObj = stage.globalStageObjectList[i];
291                                 aData.append(i, anObj.origin.x.toString() + "|" + anObj.origin.y.toString() + "|" + anObj.movingSpeed.x.toString() + "|" +  anObj.movingSpeed.y.toString() + "|" + anObj.className + "|" + parseArrayToStringSource(anObj.attribute) + "|" + parseArrayToStringSource(anObj.constructorArgs));
292                         }
293                         //送信
294                         this.networkManager.RequestObjectDisableCache(request);
295                         request.send(aData.data);
296                         //レスポンス確認
297                         if(request.status == 0){
298                                 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
299                         }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
300                                 var res = request.responseText;
301                                 if(isValidResponseText(res)){
302                                         var retArray = eval(res);
303                                         this.timeStamp = retArray[0];
304                                         //割り当てられたobjectIDを設定する
305                                         for(var i = 0; i < stage.globalStageObjectList.length; i++){
306                                                 stage.globalStageObjectList[i].objectID = retArray[1][i];
307                                         }
308                                         //すでにステージ上にある他のユーザーのオブジェクトを追加する
309                                         for(var i = 0; i < retArray[2].length; i++){
310                                                 var anArray = retArray[2][i];
311                                                 var args = eval(anArray[7]);
312                                                 var anObject = eval("new " + anArray[5] + "(stage, args);");
313                                                 if(anObject){
314                                                         this.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
315                                                         anObject.objectID = anArray[0];
316                                                         anObject.origin.x = anArray[1];
317                                                         anObject.origin.y = anArray[2];
318                                                         anObject.movingSpeed.x = anArray[3];
319                                                         anObject.movingSpeed.y = anArray[4];
320                                                         anObject.attribute = eval(anArray[6]);
321                                                         stage.addStageObject(anObject, true);
322                                                 }
323                                         }
324                                 }
325                         }else{
326                                 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
327                         }
328                         
329                 }
330                 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
331                 mainManager.runningStage = stage;
332         },
333         pauseStage: function(func){
334                 //ステージの実行を一時停止する。一時停止中、funcに指定された関数が毎tick毎に呼ばれる
335                 if(this.stagePausedFunction == null){
336                         this.stagePausedFunction = func;
337                         return true;
338                 } else{
339                         //ステージが一時停止中のfunc()の中から二重にpauseStage()を呼んではいけない
340                         return false;
341                 }
342         },
343         resumeStage: function(){
344                 //ステージの実行を再開する
345                 if(this.stagePausedFunction != null) {
346                         //必ずpauseStage()の引数に指定したfunc()の中から呼ばれる・・・はず。
347                         this.stagePausedFunction = null;
348                         return true;
349                 } else{
350                         return false;
351                 }
352         },
353         stopStage: function(){
354                 //現在実行中のステージを終了する
355                 if(this.runningStage){
356                         //runningStageから設定解除することで、イベントの通知は行われなくなる。
357                         var aGameStage = this.runningStage;
358                         this.runningStage = null;
359                         //GameStage側の終了処理を行わせる。
360                         aGameStage.stopStage();
361                         //GameStageインスタンスからGameManagerの情報を削除する。
362                         aGameStage.manager = null;
363                         aGameStage.mainCanvas = null;
364                         aGameStage.debugCanvas = null;
365                         aGameStage.mainContext = null;
366                         aGameStage.debugContext = null;
367                 }
368         },
369         loadStageFromLocal: function(code){
370                 URL_PCD_Root = "./";
371                 URL_PCD_Auth = URL_PCD_Root + "auth.php";
372                 URL_PCD_Audio = URL_PCD_Root + "audio/";
373                 URL_PCD_Stage = URL_PCD_Root + "stage/";
374                 var stage = eval(code);
375                 mainManager.runStage(stage);
376         },
377         loadStageFromNetwork: function(name){
378                 //URL_PCD_Stage/name.jsを利用してステージを作成する。
379                 request = this.networkManager.CreateRequestObject();
380                 //同期モード
381                 request.open('GET', URL_PCD_Stage + name + ".js", false);
382                 this.networkManager.RequestObjectDisableCache(request);
383                 request.send(null);
384                 
385                 if(request.status == 0){
386                         alert("ネットワークにアクセスできません。" + request.status + ":" + request.statusText);
387                 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
388                         var stage = eval(request.responseText);
389                         mainManager.runStage(stage);
390                 }else{
391                         alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
392                 }
393                 //なんでここ以下の文がIEでは実行されないの????
394                 //
395         },
396         debugOut: function(str){
397                 if(this.isIE)
398                 {
399                         //CRLF
400                         this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
401                 } else{
402                         //LF
403                         this.debugText.innerHTML = str + this.debugText.value;
404                 }
405         },
406         isAvailableBrowser: function(){
407                 //ブラウザの判別を行う。実行不可能な場合はfalseを返す。
408                 //http://d.hatena.ne.jp/Naotsugu/20110927/1317140891
409                 var userAgent = window.navigator.userAgent.toLowerCase();
410                 var appVersion = window.navigator.appVersion.toLowerCase();
411                 
412                 if (userAgent.indexOf('opera') != -1) {
413                         //opera
414                         this.debugOut("Browser:Opera\n");
415                 } else if (userAgent.indexOf('msie') != -1) {
416                         if (appVersion.indexOf("msie 9.") != -1) {
417                                 //ie9
418                                 this.debugOut("Browser:IE9\n");
419                         } else if (appVersion.indexOf("msie 8.") != -1) {
420                                 //ie8
421                                 this.debugOut("Browser:IE8\n");
422                         } else if (appVersion.indexOf("msie 7.") != -1) {
423                                 //ie7
424                                 this.debugOut("Browser:IE7\n");
425                         } else if (appVersion.indexOf("msie 6.") != -1) {
426                                 //ie6
427                                 this.debugOut("Browser:IE6\n");
428                         } else{
429                                 this.debugOut("Browser:IE?\n");
430                         }
431                         this.isIE = true;
432                 } else if (userAgent.indexOf('chrome') != -1) {
433                         //chrome
434                         this.debugOut("Browser:Chrome\n");
435                 } else if (userAgent.indexOf('safari') != -1) {
436                         //safari
437                         this.debugOut("Browser:Safari\n");
438                 } else if (userAgent.indexOf('gecko') != -1) {
439                         //gecko
440                         this.debugOut("Browser:Gecko\n");
441                 } else {
442                         //unknown
443                         this.debugOut("Browser:Unknown\n");
444                 }
445                 //this.debugOut(userAgent + "::" + appVersion + "\n");
446                 //描画コンテキストからHTML5対応チェック
447                         if(!this.mainCanvas || !this.mainCanvas.getContext){
448                                 //HTML5未対応の場合
449                                 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザ(Google Chrome等)でアクセスしてください...。");
450                         return false;
451                 }
452                 return true;
453         },
454         setBackgroundMusic: function(name){
455                 //nameは拡張子を含まない
456                 if(this.backgroundMusic){
457                         //再生していたら止める
458                         this.backgroundMusic.pause();
459                         this.backgroundMusic = null;
460                 }
461                 if(name){
462                         //引数がnullでなければaudioオブジェクトを取得
463                         this.backgroundMusic = createAudio(name);
464                         if(this.backgroundMusic){
465                                 //ループを設定して再生開始
466                                 this.backgroundMusic.loop = true;
467                                 this.backgroundMusic.play();
468                         }
469                 }
470         },
471 };
472
473 //
474 //イベントリスナー
475 //
476 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトまたはwindowになり、通常とは異なるので注意。
477
478 function keyDownEventListener(event)
479 {
480         keyDownEventListener.manager.keyDown(event);
481 }
482
483 function keyUpEventListener(event)
484 {
485         keyUpEventListener.manager.keyUp(event);
486         event.preventDefault();
487 }
488
489 function timerTickEventListener(event)
490 {
491         timerTickEventListener.manager.timerTick(event);
492 }
493
494 function timeStampTimerTickEventListener(event)
495 {
496         timerTickEventListener.manager.timeStampTimerTick(event);
497 }