2 ****PCD-2013 GameScriptCoreLibrary****
3 Tokyo Gakugei University Senior High School.
6 Mac OSX: Safari, Chrome
7 Windows: Chrome, IE(10 or later)
27 //var URL_PCD_Root = "http://localhost/pcd2013dev/www/";
28 //var URL_PCD_Root = "http://192.168.6.242/pcd2013dev/www/";
29 //var URL_PCD_Root = "http://192.168.0.3/PCD2013GSCL/www/";
30 var URL_PCD_Root = "http://192.168.6.242/pcd2013hikarupsp/pcd2013dev/www/";
31 var URL_PCD_Auth = URL_PCD_Root + "auth.php";
32 var URL_PCD_Stage = URL_PCD_Root + "stage/";
33 var URL_PCD_Stage_Local = "stage/";
39 function GameManager(){
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.debugCanvas = createCanvas("DebugCanvas", 640, 480, 0, 480, 2, "MainArea");
50 this.mainContext = this.mainCanvas.getContext('2d');
51 this.debugContext = this.debugCanvas.getContext('2d');
52 this.debugText = document.getElementById('DebugText');
53 //実行中のGameStageオブジェクトを格納
54 this.runningStage = null;
55 //現在存在しているWidghetのリストを格納
56 this.runningWidghets = [];
61 this.tickPerSecond = 60;
62 this.updateIntervalMs = 128;
63 //キーボード状態を格納するプロパティの設定
64 this.keyState = new Object();
65 this.keyState.upArrow = false;
66 this.keyState.downArrow = false;
67 this.keyState.leftArrow = false;
68 this.keyState.rightArrow = false;
73 //**描画コンテキスト取得、設定・HTML5対応チェック**
74 if(!this.mainCanvas || !this.mainCanvas.getContext){
76 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザでアクセスしてください...。");
80 //**Canvas描画コンテキストの初期設定**
82 this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
83 this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
85 this.debugContext.fillStyle = "rgb(255,255,255)";
86 this.debugContext.strokeStyle = "rgb(0, 0, 0)";
87 this.debugContext.font = "normal 20px sans-serif";
89 // pauseStage()が呼ばれたときにnullじゃなくなる
90 this.stagePausedFunction = null;
93 //コールバックを行うために、イベントリスナーのmanagerプロパティにGameManagerのインスタンスを代入する。
95 keyDownEventListener.manager = this;
96 window.addEventListener('keydown', keyDownEventListener, true);
98 keyUpEventListener.manager = this;
99 window.addEventListener('keyup', keyUpEventListener, true);
101 timerTickEventListener.manager = this;
102 window.setInterval(timerTickEventListener, 1000/this.tickPerSecond);
103 timeStampTimerTickEventListener.manager = this;
104 window.setInterval(timeStampTimerTickEventListener, this.updateIntervalMs);
106 GameManager.prototype = {
108 //prototype以下のプロパティは、新規インスタンスに参照が引き継がれる。
109 keyDown: function(event){
111 //コールバックではなくコールバック関数(keyDownEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
112 keyCode = event.keyCode;
113 switch(event.keyCode){
116 this.keyState.upArrow = true;
120 this.keyState.downArrow = true;
124 this.keyState.leftArrow = true;
128 this.keyState.rightArrow = true;
133 if(this.runningStage){
134 this.runningStage.keyDown(event);
135 switch(event.keyCode){
140 event.preventDefault();
145 keyUp: function(event){
147 //コールバックではなくコールバック関数(keyUpEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
148 keyCode = event.keyCode;
149 switch(event.keyCode){
152 this.keyState.upArrow = false;
156 this.keyState.downArrow = false;
160 this.keyState.leftArrow = false;
164 this.keyState.rightArrow = false;
168 if(this.runningStage){
169 this.runningStage.keyUp(event);
172 timerTick: function(){
177 if(this.runningStage){
178 if(this.stagePausedFunction == null)
180 this.runningStage.timerTick();
181 for(var i = 0; i < this.runningWidghets.length; i++){
182 var w = this.runningWidghets[i];
184 // Widghetのtick()からfalseで帰ってきたらWidghetを開放
185 this.runningWidghets.splice(i, 1);
191 this.stagePausedFunction();
193 this.runningStage.draw();
194 for(var w in this.runningWidghets){
199 timeStampTimerTick: function(){
201 this.timeStamp += this.updateIntervalMs;
205 if(this.userID != 0){
206 request = this.networkManager.CreateRequestObject();
208 request.open('POST', URL_PCD_Root + "update.php?uid=" + this.userID + "&action=refresh", false);
209 request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
211 aData = new RequestData();
212 for(i = 0; i < this.runningStage.globalStageObjectList.length; i++){
213 anObj = this.runningStage.globalStageObjectList[i];
214 if(anObj.ownerUID == this.userID){
215 aData.append(i, anObj.objectID + "," + anObj.origin.x + "," + anObj.origin.y + "," + anObj.movingSpeed.x + "," + anObj.movingSpeed.y+ "," + anObj.attribute);
219 this.networkManager.RequestObjectDisableCache(request);
220 request.send(aData.data);
222 if(request.status == 0){
223 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
224 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
225 var res = request.responseText;
226 if(isValidResponseText(res)){
227 var retArray = eval(res);
228 this.timeStamp = retArray[0];
230 for(var i = 0; i < retArray[3].length; i++){
231 this.debugOut("Network:removeObject:ObjectID=" + retArray[3][i] + "\n");
232 var anObject = this.runningStage.getGlobalStageObject(retArray[3][i]);
234 this.runningStage.removeStageObject(anObject);
238 for(var i = 0; i < retArray[1].length; i++){
239 //this.debugOut("Network:refreshObject:ObjectID=" + retArray[1][i] + "\n");
240 anArray = retArray[1][i];
241 var anObject = this.runningStage.getGlobalStageObject(anArray[0]);
243 anObject.origin.x = anArray[1];
244 anObject.origin.y = anArray[2];
245 anObject.movingSpeed.x = anArray[3];
246 anObject.movingSpeed.y = anArray[4];
250 for(var i = 0; i < retArray[2].length; i++){
251 anArray = retArray[2][i];
252 var args = eval(anArray[7]);
253 var anObject = eval("new " + anArray[5] + "(this.runningStage, args);");
255 this.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
256 anObject.objectID = anArray[0];
257 anObject.origin.x = anArray[1];
258 anObject.origin.y = anArray[2];
259 anObject.movingSpeed.x = anArray[3];
260 anObject.movingSpeed.y = anArray[4];
261 this.runningStage.addStageObject(anObject, true);
266 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
271 addWidghet: function(w){
273 this.runningWidghets.push(w);
275 runStage: function(stage){
276 //****新たなステージを開始する****
277 //実行中のステージがあれば終了処理を行わせる。
278 if(this.runningStage){
281 //**新たに開始するステージの初期化**
282 //GameManager側の情報をGameStageに渡す。
283 stage.manager = this;
284 stage.mainCanvas = this.mainCanvas
285 stage.debugCanvas = this.debugCanvas
286 stage.mainContext = this.mainContext
287 stage.debugContext = this.debugContext
288 //GameStage側の初期化処理を行わせる。
292 if(this.userID != 0){
293 request = this.networkManager.CreateRequestObject();
295 request.open('POST', URL_PCD_Root + "update.php?uid=" + this.userID + "&action=add", false);
296 request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
298 aData = new RequestData();
299 for(i = 0; i < stage.globalStageObjectList.length; i++){
300 anObj = stage.globalStageObjectList[i];
301 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));
304 this.networkManager.RequestObjectDisableCache(request);
305 request.send(aData.data);
307 if(request.status == 0){
308 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
309 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
310 var res = request.responseText;
311 if(isValidResponseText(res)){
312 var retArray = eval(res);
313 this.timeStamp = retArray[0];
314 for(var i = 0; i < stage.globalStageObjectList.length; i++){
315 stage.globalStageObjectList[i].objectID = retArray[1][i];
317 for(var i = 0; i < retArray[2].length; i++){
318 var anArray = retArray[2][i];
319 var args = eval(anArray[7]);
320 var anObject = eval("new " + anArray[5] + "(stage, args);");
322 this.debugOut("Network:addObject:ObjectID=" + anArray[0] + "\n");
323 anObject.objectID = anArray[0];
324 anObject.origin.x = anArray[1];
325 anObject.origin.y = anArray[2];
326 anObject.movingSpeed.x = anArray[3];
327 anObject.movingSpeed.y = anArray[4];
328 anObject.attribute = eval(anArray[6]);
329 stage.addStageObject(anObject, true);
334 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
338 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
339 this.runningStage = stage;
341 pauseStage: function(func){
342 //ステージの実行を一時停止する。一時停止中、funcに指定された関数が毎tick毎に呼ばれる
343 if(this.stagePausedFunction == null)
345 this.stagePausedFunction = func;
349 //ステージが一時停止中のfunc()の中から二重にpauseStage()を呼んではいけない
353 resumeStage: function(){
354 if(this.stagePausedFunction != null)
356 //必ずpauseStage()の引数に指定したfunc()の中から呼ばれる・・・はず。
357 this.stagePausedFunction = null;
364 stopStage: function(){
365 //****現在実行中のステージを終了する****
366 if(this.runningStage){
367 //runningStageから解除することで、イベントの通知は行われなくなる。
368 var aGameStage = this.runningStage;
369 this.runningStage = null;
370 //GameStage側の終了処理を行わせる。
371 aGameStage.stopStage();
372 //GameStageインスタンスからGameManagerの情報を削除する。
373 aGameStage.manager = null;
374 aGameStage.mainCanvas = null;
375 aGameStage.debugCanvas = null;
376 aGameStage.mainContext = null;
377 aGameStage.debugContext = null;
380 loadStageFromLocal: function(code, onLoaded){
381 var ____stage = eval(code);
382 mainManager.runStage(____stage);
384 loadStageFromNetwork: function(name){
385 //urlに存在するjavascriptファイルを利用してステージを作成する。
386 request = this.networkManager.CreateRequestObject();
388 request.open('GET', URL_PCD_Stage + name + ".js", false);
389 this.networkManager.RequestObjectDisableCache(request);
391 if(request.status == 0){
392 alert("ネットワークにアクセスできません。" + request.status + ":" + request.statusText);
393 }else if((200 <= request.status && request.status < 300) || (request.status == 304)){
394 var stage = eval(request.responseText);
395 mainManager.runStage(stage);
397 alert("サーバーがエラーを返しました。" + request.status + ":" + request.statusText);
400 debugOut: function(str){
401 if(!/*@cc_on!@*/false)
403 this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
405 this.debugText.innerHTML = str + this.debugText.value;
408 checkBrowser: function(){
409 //http://d.hatena.ne.jp/Naotsugu/20110927/1317140891
410 var userAgent = window.navigator.userAgent.toLowerCase();
411 var appVersion = window.navigator.appVersion.toLowerCase();
413 if (userAgent.indexOf('opera') != -1) {
415 this.debugOut("Browser:Opera\n");
416 } else if (userAgent.indexOf('msie') != -1) {
417 if (appVersion.indexOf("msie 9.") != -1) {
419 this.debugOut("Browser:IE9\n");
420 } else if (appVersion.indexOf("msie 8.") != -1) {
422 this.debugOut("Browser:IE8\n");
423 } else if (appVersion.indexOf("msie 7.") != -1) {
425 this.debugOut("Browser:IE7\n");
426 } else if (appVersion.indexOf("msie 6.") != -1) {
428 this.debugOut("Browser:IE6\n");
430 this.debugOut("Browser:IE?\n");
432 } else if (userAgent.indexOf('chrome') != -1) {
434 this.debugOut("Browser:Chrome\n");
435 } else if (userAgent.indexOf('safari') != -1) {
437 this.debugOut("Browser:Safari\n");
438 } else if (userAgent.indexOf('gecko') != -1) {
440 this.debugOut("Browser:Gecko\n");
443 this.debugOut("Browser:Unknown\n");
445 this.debugOut(userAgent + "::" + appVersion + "\n");
453 function Point2D(x, y){
457 Point2D.prototype = {
461 function Rectangle(x, y, width, height){
462 this.origin = new Point2D(x,y);
463 this.size = new Point2D(width,height);
465 Rectangle.prototype = {
469 function ResourceManager(){
471 this.resourceObjectList = new Array();
473 this.ResourceTag.prototype = {
477 ResourceManager.prototype = {
479 addAudioResource: function(id, src){
480 dobj = document.createElement("audio");
481 parent = document.getElementById("Resources");
483 parent.appendChild(dobj);
485 this.resourceObjectList.push(dobj);
487 dobj.isLoaded = false;
488 dobj.onload = this.resourceLoaded;
491 resourceLoaded: function(){
492 //コールバック関数のthisはコールバック関数の設定先オブジェクト(DOMObject)となる。
493 this.isLoaded = true;
495 waitForLoadResource: function(){
497 for(i = 0; i < resourceObjectList.length; i++){
498 if(!resourceObjectList[i].isLoaded){
503 if(i == resourceObjectList.length){
510 function NetworkManager(){
513 NetworkManager.prototype = {
514 //from http://hakuhin.jp/js/xmlhttprequest.html
515 CreateRequestObject: function(){
519 // XMLHttpRequest オブジェクトを作成
520 rq = new XMLHttpRequest();
524 rq = new ActiveXObject('MSXML2.XMLHTTP.6.0');
527 rq = new ActiveXObject('MSXML2.XMLHTTP.3.0');
530 rq = new ActiveXObject('MSXML2.XMLHTTP');
537 RequestObjectDisableCache: function(rq){
538 //call after open request.
540 //http://vird2002.s8.xrea.com/javascript/XMLHttpRequest.html
541 rq.setRequestHeader('Pragma', 'no-cache'); // HTTP/1.0 における汎用のヘッダフィールド
542 rq.setRequestHeader('Cache-Control', 'no-cache'); // HTTP/1.1 におけるキャッシュ制御のヘッダフィールド
543 rq.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
548 function RequestData(){
551 RequestData.prototype = {
552 append: function(name, data){
553 this.data += this.getURLEncodedString(name) + "=" + this.getURLEncodedString(data) + "&";
555 getURLEncodedString: function(str){
556 return encodeURIComponent(str).replace(/%20/g, '+');
564 function createCanvas(id, width, height, x, y, z, parent)
567 //width * heightの大きさのCanvasを
569 //parentには、Canvasタグを包含することになるDOMオブジェクトのidを指定する。
570 canvas = document.createElement("canvas");
571 parent = document.getElementById(parent);
575 parent.appendChild(canvas);
577 canvas.style.position = "absolute";
578 canvas.style.top = y + "px";
579 canvas.style.left = x + "px";
580 canvas.style.zIndex = z;
582 canvas.width = width;
583 canvas.height = height;
588 function createDOMObject(typestr, idstr, parentidstr)
590 dobj = document.createElement(typestr);
591 parentObj = document.getElementById(parentidstr);
594 parentObj.appendChild(dobj);
599 function destroyDOMObjectByID(id)
601 //識別名idのDOMオブジェクトを破棄する。
602 object = document.getElementById(id);
603 parentObj = object.parentNode;
605 parentObj.removeChild(object);
608 function removeObjectFromArray(anArray, anObject)
610 //anArray中にある全てのanObjectを削除し、空いた部分は前につめる。
611 for(var i = 0; i < anArray.length; i++){
612 if(anArray[i] == anObject){
613 anArray.splice(i, 1);
618 function parseArrayToStringSource(anArray){
619 //parseArrayToStringSource([1,"321a", "abc", ["cder", "", 554]]);
626 for(var i = 0; i < anArray.length; i++){
627 if(anArray[i] instanceof Array){
628 srcstr += parseArrayToStringSource(anArray[i]) + ",";
629 } else if(!isNaN(anArray[i]) && anArray[i].toString().replace(/\s+/g, "").length > 0){
630 //isNaNだけでは数値判定できないので、文字列化後の空白文字を削除した長さも検査している。
631 srcstr += anArray[i] + ",";
633 srcstr += "'" + anArray[i] + "',";
639 srcstr = srcstr.slice(0, srcstr.length - 1);
646 function isValidResponseText(res)
648 if(res.indexOf("Warning") == -1){
649 if(res.indexOf("error") == -1){
653 alert("サーバースクリプトがエラーを返しました。:" + res);
654 mainManager.stopStage();
661 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトになり、通常とは異なるので注意。
663 function keyDownEventListener(event)
665 keyDownEventListener.manager.keyDown(event);
668 function keyUpEventListener(event)
670 keyUpEventListener.manager.keyUp(event);
671 event.preventDefault();
674 function timerTickEventListener(event)
676 timerTickEventListener.manager.timerTick(event);
679 function timeStampTimerTickEventListener(event)
681 timerTickEventListener.manager.timeStampTimerTick(event);
688 function drawText(gcontext, text, x, y)
691 //前景をstrokeStyleで塗りつぶした文字列を描画する
693 //fillTextの座標は文字列の左下!
694 textsize = gcontext.measureText(text);
695 gcontext.fillRect(x, y, textsize.width, 20);
697 gcontext.fillStyle = gcontext.strokeStyle;
698 gcontext.fillText(text, x, y + 20 - 1);
702 function drawArcDegree(gcontext, radius, startAngleDegree, endAngleDegree, x, y, anticlockwise)
704 //半径radius, 中心座標(x,y)の円弧の、
705 //startAngleDegreeからendAngleDegreeまでの範囲を、
706 //(!anticlockwise)=時計回り
707 //(anticlockwise) =反時計回り
710 startAngleRadian = startAngleDegree * Math.PI / 180;
711 endAngleRadian = endAngleDegree * Math.PI / 180;
713 gcontext.beginPath();
714 gcontext.arc(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(radius), startAngleRadian, endAngleRadian, anticlockwise);
717 gcontext.closePath();
720 function fillRect(gContext, x, y, w, h)
722 gContext.fillRect(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(w), drawCoordinatesInInteger(h));
725 function strokeRect(gContext, x, y, w, h)
727 gContext.strokeRect(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(w), drawCoordinatesInInteger(h));
730 function drawCoordinatesInInteger(coordinateElement)
732 //from http://www.html5rocks.com/ja/tutorials/canvas/performance/
733 // With a bitwise or.
734 return ((0.5 + coordinateElement) | 0);