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 loc = document.location.href;
30
31 var URL_PCD_Root = loc.slice(0, loc.lastIndexOf("/") + 1);
32
33 //var URL_PCD_Root = "http://localhost/pcd2013dev/www/";
34 //var URL_PCD_Root = "http://192.168.6.242/pcd2013dev/www/";
35 //var URL_PCD_Root = "http://192.168.0.3/PCD2013GSCL/www/";
36 //var URL_PCD_Root = "http://192.168.6.242/pcd2013hikarupsp/pcd2013dev/www/";
37 var URL_PCD_Auth = URL_PCD_Root + "auth.php";
38 var URL_PCD_Audio = URL_PCD_Root + "audio/";
39 var URL_PCD_Stage = URL_PCD_Root + "stage/";
40
41 // ゲームを呼び出す関数       適切なdiv要素で呼び出すとゲームを初期化できる。
42 // つまり、自作ゲームをしたい場合
43 // audio, corelib, images, stageの各フォルダをコピーしたdirにhtmlを置き、<div>.InitGameManager() と実行するスクリプトを書く
44 // stageName == nullだとステージを開始しない。指定する時は.jsおよびパスを省く
45
46 //
47 var CollideTop = 8;
48 var CollideBottom = 4;
49 var CollideLeft = 2;
50 var CollideRight = 1;
51
52 HTMLDivElement.prototype.InitGameManager = function(stageName)
53 {
54         if(this instanceof HTMLDivElement)
55         {
56                 var man = new GameManager(this, null);
57                 if(stageName) man.loadStageFromNetwork.apply(man, [stageName]);
58                 return man;
59         }else
60         {
61                 throw new TypeError("InitGameManager はdiv要素にしか実行できません");
62         }
63 };
64
65 //
66 //ゲームマネージャー
67 //
68
69 function GameManager(parent, debugTextName){
70         
71         //引数チェック
72         if(debugTextName == undefined) debugTextName = "DebugText";
73         if(parent == undefined) parent = document.getElementById("MainArea");
74         
75         //parentの初期設定
76         if(parent.style.position != 'absolute') parent.style.position = 'relative';
77         
78         this.userID = 0;
79         //サブマネージャーの設定
80         this.networkManager = new NetworkManager(this);
81         this.UIManager = new UIManager(this);
82         this.userManager = new UserManager(this);
83         this.mainArea = parent;
84         //必要最低限のCanvasとコンテキストの設定
85         this.mainCanvas = createCanvas("MainCanvas", 640, 480, 0, 0, 1, parent);
86         this.mainCanvas.style.border = "solid 1px";
87         this.mainContext = this.mainCanvas.getContext('2d');
88         this.debugText = document.getElementById(debugTextName);        //要素が存在しないとnullになり、デバッグが無効になる
89         if(!this.debugText) this.debugText = null;
90         
91         //ブラウザチェック
92         this.isIE = false;
93         if(!this.isAvailableBrowser()){
94                 return null;
95         }
96         //描画コンテキストの初期設定
97         this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
98         this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
99         this.mainContext.font = "normal 20px sans-serif";
100         //実行中のGameStageオブジェクトを格納
101         this.runningStage = null;
102         this.runningStageName = null;
103         //現在存在しているWidghetのリストを格納
104         this.runningWidghets = [];
105         
106         //タイマーカウントを初期化
107         this.tickCount = 0;
108         //タイマーカウントの秒あたりの回数を設定
109         this.tickPerSecond = 60;
110         // pauseStage()が呼ばれたときにnullじゃなくなる
111         this.stagePausedFunction = null;
112         
113         this.backgroundMusic = null;
114         this.addWidget(new UserStateWidgetClass(this));
115         //**イベントリスナー設定**
116         //コールバックを行うために、イベントリスナーのmanagerプロパティにGameManagerのインスタンスを代入する。
117         //timerTick
118         timerTickEventListener.manager = this;
119         window.setInterval(timerTickEventListener, 1000/this.tickPerSecond);
120 }
121 GameManager.prototype = {
122         timerTick: function(){
123                 //各オブジェクトの単位時間ごとの動作と再描画を行う
124                 //単位時間ごとの動作
125                 this.tickCount++;
126                 if(this.stagePausedFunction == null){
127                         //ポーズしていなければ更新処理
128                         if(this.runningStage){
129                                 //ステージ
130                                 this.runningStage.timerTick();
131                         }
132                 }
133                 // runningStage.timerTick() 内でpauseStage()された時、ここで再度判定しないとWidghetのtickが実行されてしまう
134                 if(this.stagePausedFunction == null){
135                         //ウィジェット
136                         for(var i = 0; i < this.runningWidghets.length; i++){
137                                 var w = this.runningWidghets[i];
138                                 if(!w.tick()){
139                                         // Widghetのtick()からfalseで帰ってきたらWidghetを開放
140                                         this.removeWidget(w);
141                                         i--;
142                                 }
143                         }
144                 } else{
145                         //ポーズしているならば処理関数を実行
146                         this.stagePausedFunction();
147                 }
148                 
149                 //描画処理
150                 if(this.runningStage){
151                         //ステージ
152                         this.runningStage.draw();
153                 }
154                 //ウィジェット
155                 for(var i = 0; i < this.runningWidghets.length; i++){
156                         var w = this.runningWidghets[i];
157                         w.draw();
158                 }
159                 
160                 //各オブジェクトの走査が終わってから、死亡判定およびステージ再読み込みを行う。
161                 if(this.userManager.HP == 0){
162                         alert("死んでしまった…");
163                         this.UIManager.clearInput();
164                         if(this.runningStageName){
165                                 this.loadStageFromNetwork(this.runningStageName);
166                         } else{
167                                 //ローカルモード時は動作を停止させるだけ
168                                 this.stopStage();
169                         }
170                         this.userManager.HP = this.userManager.max_HP;
171                 }
172         },
173         addWidget: function(w){
174                 w.attach();
175                 this.runningWidghets.push(w);
176         },
177         removeWidget: function(w){
178                 removeObjectFromArray(this.runningWidghets, w);
179                 w.detach();
180         },
181         runStage: function(stage){
182                 //新たなステージを開始する
183                 //実行中のステージがあれば終了処理を行わせる。
184                 if(this.runningStage){
185                         this.stopStage();
186                 }
187                 //新たに開始するステージの初期化
188                 //GameManager側の情報をGameStageに渡す。
189                 stage.manager = this;
190                 stage.mainCanvas = this.mainCanvas
191                 stage.debugCanvas = this.debugCanvas
192                 stage.mainContext = this.mainContext
193                 stage.debugContext = this.debugContext
194                 //GameStage側の初期化処理を行わせる。
195                 stage.runStage();
196                 //ネットワーク同期初期化
197                 this.networkManager.joinStage(stage);
198                 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
199                 this.runningStage = stage;
200         },
201         pauseStage: function(func){
202                 //ステージの実行を一時停止する。一時停止中、funcに指定された関数が毎tick毎に呼ばれる
203                 if(this.stagePausedFunction == null){
204                         this.stagePausedFunction = func;
205                         return true;
206                 } else{
207                         //ステージが一時停止中のfunc()の中から二重にpauseStage()を呼んではいけない
208                         return false;
209                 }
210         },
211         resumeStage: function(){
212                 //ステージの実行を再開する
213                 if(this.stagePausedFunction != null) {
214                         //必ずpauseStage()の引数に指定したfunc()の中から呼ばれる・・・はず。
215                         this.stagePausedFunction = null;
216                         return true;
217                 } else{
218                         return false;
219                 }
220         },
221         stopStage: function(){
222                 //現在実行中のステージを終了する
223                 if(this.runningStage){
224                         //runningStageから設定解除することで、イベントの通知は行われなくなる。
225                         var aGameStage = this.runningStage;
226                         this.runningStage = null;
227                         //GameStage側の終了処理を行わせる。
228                         aGameStage.stopStage();
229                         //GameStageインスタンスからGameManagerの情報を削除する。
230                         aGameStage.manager = null;
231                         aGameStage.mainCanvas = null;
232                         aGameStage.debugCanvas = null;
233                         aGameStage.mainContext = null;
234                         aGameStage.debugContext = null;
235                 }
236         },
237         loadStageFromLocal: function(code){
238                 //各種パスをローカル用に変更
239                 URL_PCD_Root = "./";
240                 URL_PCD_Auth = URL_PCD_Root + "auth.php";
241                 URL_PCD_Audio = URL_PCD_Root + "audio/";
242                 URL_PCD_Stage = URL_PCD_Root + "stage/";
243                 
244                 var stage = eval(code);
245                 mainManager.runStage(stage);
246         },
247         loadStageFromNetwork: function(name){
248                 //URL_PCD_Stage/name.jsを利用してステージを作成する。
249                 request = this.networkManager.CreateRequestObject();
250                 //同期モード
251                 request.open('GET', URL_PCD_Stage + name + ".js", false);
252                 this.networkManager.RequestObjectDisableCache(request);
253                 request.send(null);
254                 
255                 if(request.status == 0){
256                         alert("ネットワークにアクセスできません。" + request.status + ":" + request.statusText);
257                 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
258                         var stage = eval(request.responseText);
259                         this.runStage(stage);
260                         this.runningStageName = name;
261                 }else{
262                         alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
263                 }
264         },
265         debugOut: function(str){
266                 if(this.debugText != null)
267                 {
268                         if(this.isIE)
269                         {
270                                 //CRLF
271                                 this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
272                         } else{
273                                 //LF
274                                 this.debugText.innerHTML = str + this.debugText.value;
275                         }
276                 }
277         },
278         isAvailableBrowser: function(){
279                 //ブラウザの判別を行う。実行不可能な場合はfalseを返す。
280                 //http://d.hatena.ne.jp/Naotsugu/20110927/1317140891
281                 var userAgent = window.navigator.userAgent.toLowerCase();
282                 var appVersion = window.navigator.appVersion.toLowerCase();
283                 
284                 if (userAgent.indexOf('opera') != -1) {
285                         //opera
286                         this.debugOut("Browser:Opera\n");
287                 } else if (userAgent.indexOf('msie') != -1) {
288                         if (appVersion.indexOf("msie 9.") != -1) {
289                                 //ie9
290                                 this.debugOut("Browser:IE9\n");
291                         } else if (appVersion.indexOf("msie 8.") != -1) {
292                                 //ie8
293                                 this.debugOut("Browser:IE8\n");
294                         } else if (appVersion.indexOf("msie 7.") != -1) {
295                                 //ie7
296                                 this.debugOut("Browser:IE7\n");
297                         } else if (appVersion.indexOf("msie 6.") != -1) {
298                                 //ie6
299                                 this.debugOut("Browser:IE6\n");
300                         } else{
301                                 this.debugOut("Browser:IE?\n");
302                         }
303                         this.isIE = true;
304                 } else if (userAgent.indexOf('chrome') != -1) {
305                         //chrome
306                         this.debugOut("Browser:Chrome\n");
307                 } else if (userAgent.indexOf('safari') != -1) {
308                         //safari
309                         this.debugOut("Browser:Safari\n");
310                 } else if (userAgent.indexOf('gecko') != -1) {
311                         //gecko
312                         this.debugOut("Browser:Gecko\n");
313                 } else {
314                         //unknown
315                         this.debugOut("Browser:Unknown\n");
316                 }
317                 //描画コンテキストからHTML5対応チェック
318                         if(!this.mainCanvas || !this.mainCanvas.getContext){
319                                 //HTML5未対応の場合
320                                 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザ(Google Chrome等)でアクセスしてください...。");
321                         return false;
322                 }
323                 return true;
324         },
325         setBackgroundMusic: function(name){
326                 if(this.backgroundMusic){
327                         //再生していたら止める
328                         this.backgroundMusic.pause();
329                         this.backgroundMusic = null;
330                 }
331                 if(name){
332                         //引数がnullでなければaudioオブジェクトを取得
333                         this.backgroundMusic = createAudio(name);
334                         if(this.backgroundMusic){
335                                 //ループを設定して再生開始
336                                 this.backgroundMusic.loop = true;
337                                 this.backgroundMusic.play();
338                         }
339                 }
340         },
341 };
342
343 //
344 //イベントリスナー
345 //
346 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトまたはwindowになり、通常とは異なるので注意。
347
348 function timerTickEventListener(event)
349 {
350         timerTickEventListener.manager.timerTick(event);
351 }