2 ****PCD-2013 GameScriptCoreLibrary****
3 Tokyo Gakugei University Senior High School.
14 document.getElementById("main").style.display = "none";
15 (mainというIDが振られている要素の表示を消す)
16 this.effectSound.pause();
17 this.effectSound.currentTime = 0;
18 this.effectSound.play();
19 this.backgroundImage = new Image();
20 this.backgroundMusic = document.getElementById('BGM006');
21 this.effectSound = document.getElementById('SE_Select');
23 this.backgroundImage.manager = this;
24 this.backgroundImage.onload = this.backgroundLoaded;
25 this.backgroundImage.src = "title.gif";
28 this.backgroundMusic.loop = true;
29 this.backgroundMusic.play();
31 MicrosoftInternetExplorerでは、ローカル変数でparentを使うとappendChildが使えなくなる…。
33 <About collision checking>(このあたりはまだドラフト)
34 ステージオブジェクトの最小単位を8x8とする。
40 var URL_PCD_Root = "http://192.168.6.242/";
41 var URL_PCD_Auth = URL_PCD_Root + "auth.php";
42 var URL_PCD_Stage = URL_PCD_Root + "stage/";
48 function GameManager(){
54 this.networkManager = new NetworkManager();
55 //必要最低限のCanvasとコンテキストの設定
56 this.mainCanvas = createCanvas("MainCanvas", 640, 480, 0, 0, 1, "MainArea");
57 this.mainCanvas.style.border = "solid 1px";
58 this.debugCanvas = createCanvas("DebugCanvas", 640, 480, 0, 480, 2, "MainArea");
59 this.mainContext = this.mainCanvas.getContext('2d');
60 this.debugContext = this.debugCanvas.getContext('2d');
61 this.debugText = document.getElementById('DebugText');
62 //実行中のGameStageオブジェクトを格納
63 this.runningStage = null;
68 this.tickPerSecond = 60;
69 //キーボード状態を格納するプロパティの設定
70 this.keyState = new Object();
71 this.keyState.upArrow = false;
72 this.keyState.downArrow = false;
73 this.keyState.leftArrow = false;
74 this.keyState.rightArrow = false;
76 //**描画コンテキスト取得、設定・HTML5対応チェック**
77 if(!this.mainCanvas || !this.mainCanvas.getContext){
79 alert("このゲームを遊ぶためには、HTML5に対応しているブラウザでアクセスしてください...。");
83 //**Canvas描画コンテキストの初期設定**
85 this.mainContext.fillStyle = "rgba(200,255,200,0.5)";
86 this.mainContext.strokeStyle = "rgba(0, 0, 0, 0.5)";
88 this.debugContext.fillStyle = "rgb(255,255,255)";
89 this.debugContext.strokeStyle = "rgb(0, 0, 0)";
90 this.debugContext.font = "normal 20px sans-serif";
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, 100);
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;
117 event.preventDefault();
121 this.keyState.downArrow = true;
122 event.preventDefault();
126 this.keyState.leftArrow = true;
127 event.preventDefault();
131 this.keyState.rightArrow = true;
132 event.preventDefault();
136 this.debugOut("keyDw:" + keyCode + "\n");
138 if(this.runningStage){
139 this.runningStage.keyDown(event);
142 keyUp: function(event){
144 //コールバックではなくコールバック関数(keyUpEventListener)から呼び出されるので、thisはGameManagerのインスタンスとなる。
145 keyCode = event.keyCode;
146 switch(event.keyCode){
149 this.keyState.upArrow = false;
153 this.keyState.downArrow = false;
157 this.keyState.leftArrow = false;
161 this.keyState.rightArrow = false;
165 this.debugOut("keyUp:" + keyCode + "\n");
167 if(this.runningStage){
168 this.runningStage.keyUp(event);
171 timerTick: function(){
175 if(this.runningStage){
176 this.runningStage.timerTick();
179 timeStampTimerTick: function(){
181 this.timeStamp += 10;
182 drawText(this.debugContext, "timeStamp:" + this.timeStamp, 0, 40);
184 runStage: function(stage){
185 //****新たなステージを開始する****
186 //実行中のステージがあれば終了処理を行わせる。
187 if(this.runningStage){
190 //**新たに開始するステージの初期化**
191 //GameManager側の情報をGameStageに渡す。
192 stage.manager = this;
193 stage.mainCanvas = this.mainCanvas
194 stage.debugCanvas = this.debugCanvas
195 stage.mainContext = this.mainContext
196 stage.debugContext = this.debugContext
197 //GameStage側の初期化処理を行わせる。
199 //runningStageに登録することで、イベントの通知が開始され、GameStageは実行状態に入る。
200 this.runningStage = stage;
202 stopStage: function(){
203 //****現在実行中のステージを終了する****
204 if(this.runningStage){
205 //runningStageから解除することで、イベントの通知は行われなくなる。
206 var aGameStage = this.runningStage;
207 this.runningStage = null;
208 //GameStage側の終了処理を行わせる。
209 aGameStage.stopStage();
210 //GameStageインスタンスからGameManagerの情報を削除する。
211 aGameStage.manager = null;
212 aGameStage.mainCanvas = null;
213 aGameStage.debugCanvas = null;
214 aGameStage.mainContext = null;
215 aGameStage.debugContext = null;
218 loadStageFromNetwork: function(name, onLoaded){
219 //urlに存在するjavascriptファイルを利用してステージを作成する。
220 request = this.networkManager.CreateRequestObject();
221 request.onLoaded = onLoaded;
222 request.onreadystatechange = this.loadStageFromNetwork_HTTPStateChange
223 request.open('GET', URL_PCD_Stage + name + ".js");
226 loadStageFromNetwork_HTTPStateChange: function(){
227 //requestコールバックなのでthisはrequest!
228 switch(this.readyState){
230 //console.log("XMLHttpRequest created.");
233 //console.log("open() called.");
236 //console.log("Response header received.");
239 //console.log("Response body receiving.");
242 //mainManager.debugOut("send() compleated.\n");
243 //mainManager.debugOut("status:" + this.status + ":" + this.statusText + "\n");
244 if(this.status == 0){
245 alert("ネットワークにアクセスできません。" + this.status + ":" + this.statusText);
246 }else if((200 <= this.status && this.status < 300) || (this.status == 304)){
247 //console.log("ACK");
248 stage = new GameStage();
249 eval(this.responseText);
250 if(this.onLoaded != null){
251 this.onLoaded(stage);
253 mainManager.runStage(stage);
256 alert("サーバーがエラーを返しました。" + this.status + ":" + this.statusText);
261 debugOut: function(str){
262 if(!/*@cc_on!@*/false)
264 this.debugText.value = str.replace(/\n/g,"\r\n") + this.debugText.value;
268 this.debugText.innerHTML = str + this.debugText.value;
277 function GameStage(){
282 //GameManagerから渡されるプロパティ
284 this.mainCanvas = null;
285 this.debugCanvas = null;
286 this.mainContext = null;
287 this.debugContext = null;
289 this.stageObjectList = new Array();
290 //オーバーライドされる可能性のある関数の保存
291 //this.super = new Object();
292 //上記の方法では、thisがsuperになってしまい動作しないので、同一階層に関数の参照を作成することになった。
293 this.super_keyDown = this.keyDown;
294 this.super_keyUp = this.keyUp;
295 this.super_timerTick = this.timerTick;
296 this.super_runStage = this.runStage;
297 this.super_stopStage = this.stopStage;
298 this.super_addStageObject = this.addStageObject;
299 this.super_removeStageObject = this.removeStageObject;
301 this.collisionMapCanvas = null;
302 this.collisionMapContext = null;
304 GameStage.prototype = {
305 //以下の関数をオーバーライドしてステージを作成する。
306 keyDown: function(event){
309 keyUp: function(event){
312 timerTick: function(){
316 drawText(this.debugContext, "tick:" + this.tickCount, 0, 20);
319 this.mainContext.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
320 this.collisionMapContext.clearRect(0, 0, this.collisionMapCanvas.width * 8, this.collisionMapCanvas.height * 8);
323 for(i = 0; i < this.stageObjectList.length; i++){
324 this.stageObjectList[i].display();
327 runStage: function(){
331 this.collisionMapCanvas = createCanvas("collisionMapCanvas", this.mainCanvas.width, this.mainCanvas.height, this.mainCanvas.width, 0, 1, "MainArea");
332 this.collisionMapContext = this.collisionMapCanvas.getContext('2d');
333 this.collisionMapContext.fillStyle = "rgba(0,0,0, 0.2)";
334 this.collisionMapContext.strokeStyle = "rgba(0,0,0, 0.2)";
335 this.collisionMapContext.scale(1, 1);
337 stopStage: function(){
339 destroyDOMObjectByID(this.collisionMapCanvas.id);
340 this.collisionMapCanvas = null;
341 this.collisionMapContext = null;
342 this.stageObjectList = null;
344 addStageObject: function(aStageObject){
346 this.stageObjectList.push(aStageObject);
348 removeStageObject: function(aStageObject){
350 removeObjectFromArray(this.stageObjectList, aStageObject);
358 function StageObject(aStage){
360 this.origin = new Point2D(10, 10);
361 //originを中心とした座標でのオブジェクトの描画面のサイズ
362 this.frame = new Rectangle(-8, -8, 16, 16);
363 this.movingSpeed = new Point2D(0, 0);
364 //2 * hysteresis >= movingFrictionであることを推奨する。
365 //そうでない場合、摩擦での減速後に完全に停止できない可能性がある。
366 this.movingFriction = 90;
367 this.hysteresis = this.movingFriction / 2;
368 //実体を持たない、つまり衝突判定が必要ない場合はtrue.
369 this.isPhantom = false;
370 //console.log("StageObject:Init");
372 StageObject.prototype = {
375 this.computeTickMoving();
376 this.computeTickFriction();
377 this.computeTickBounding();
379 this.stage.mainContext.save();
380 this.stage.mainContext.fillStyle = "rgba(" + (((11*this.frame.size.x) & 0x7f) + 0x80) + "," + (((19*this.frame.size.x) & 0x7f) + 0x80) + "," + (((17*this.frame.size.x) & 0x7f) + 0x80) + ",0.5)";
381 drawArcDegree(this.stage.mainContext, this.frame.size.x / 2, 0, 360, this.origin.x, this.origin.y, false);
382 this.stage.mainContext.restore();
384 strokeRect(this.stage.collisionMapContext, this.origin.x + this.frame.origin.x, this.origin.y + this.frame.origin.y, this.frame.size.x, this.frame.size.y);
386 computeTickFriction: function(){
388 if(this.movingSpeed.x < -this.hysteresis){
389 this.movingSpeed.x += this.movingFriction / this.stage.manager.tickPerSecond;
390 } else if(this.movingSpeed.x > this.hysteresis){
391 this.movingSpeed.x -= this.movingFriction / this.stage.manager.tickPerSecond;
393 this.movingSpeed.x = 0;
395 if(this.movingSpeed.y < -this.hysteresis){
396 this.movingSpeed.y += this.movingFriction / this.stage.manager.tickPerSecond;
397 } else if(this.movingSpeed.y > this.hysteresis){
398 this.movingSpeed.y -= this.movingFriction / this.stage.manager.tickPerSecond;
400 this.movingSpeed.y = 0;
403 computeTickMoving: function(){
405 this.origin.x += this.movingSpeed.x / this.stage.manager.tickPerSecond;
406 this.origin.y += this.movingSpeed.y / this.stage.manager.tickPerSecond;
408 computeTickBounding: function(){
410 if(this.origin.x < -this.frame.origin.x){
412 this.origin.x = -this.frame.origin.x;
413 this.movingSpeed.x = -this.movingSpeed.x;
414 } else if(this.origin.x > this.stage.mainCanvas.width - (this.frame.origin.x + this.frame.size.x)){
416 this.origin.x = this.stage.mainCanvas.width - (this.frame.origin.x + this.frame.size.x);
417 this.movingSpeed.x = -this.movingSpeed.x;
419 if(this.origin.y < -this.frame.origin.y){
421 this.origin.y = -this.frame.origin.y;
422 this.movingSpeed.y = -this.movingSpeed.y;
423 } else if(this.origin.y > this.stage.mainCanvas.height - (this.frame.origin.y + this.frame.size.y)){
425 this.origin.y = this.stage.mainCanvas.height - (this.frame.origin.y + this.frame.size.y);
426 this.movingSpeed.y = -this.movingSpeed.y;
435 function Point2D(x, y){
439 Point2D.prototype = {
443 function Rectangle(x, y, width, height){
444 this.origin = new Point2D(x,y);
445 this.size = new Point2D(width,height);
447 Rectangle.prototype = {
451 function ResourceManager(){
453 this.resourceObjectList = new Array();
455 this.ResourceTag.prototype = {
459 ResourceManager.prototype = {
461 addAudioResource: function(id, src){
462 dobj = document.createElement("audio");
463 parent = document.getElementById("Resources");
465 parent.appendChild(dobj);
467 this.resourceObjectList.push(dobj);
469 dobj.isLoaded = false;
470 dobj.onload = this.resourceLoaded;
473 resourceLoaded: function(){
474 //コールバック関数のthisはコールバック関数の設定先オブジェクト(DOMObject)となる。
475 this.isLoaded = true;
477 waitForLoadResource: function(){
479 for(i = 0; i < resourceObjectList.length; i++){
480 if(!resourceObjectList[i].isLoaded){
485 if(i == resourceObjectList.length){
492 function NetworkManager(){
495 NetworkManager.prototype = {
496 //from http://hakuhin.jp/js/xmlhttprequest.html
497 CreateRequestObject: function(){
500 // XMLHttpRequest オブジェクトを作成
501 return new XMLHttpRequest();
505 return new ActiveXObject('MSXML2.XMLHTTP.6.0');
508 return new ActiveXObject('MSXML2.XMLHTTP.3.0');
511 return new ActiveXObject('MSXML2.XMLHTTP');
516 HTTPStateChange: function(){
517 //requestコールバックなのでthisはrequest!
518 switch(this.readyState){
520 //console.log("XMLHttpRequest created.");
523 //console.log("open() called.");
526 //console.log("Response header received.");
529 //console.log("Response body receiving.");
532 //console.log("send() compleated.");
533 //console.log("status:" + this.status + ":" + this.statusText);
534 if(this.status == 0){
535 console.log("Error");
536 }else if((200 <= this.status && this.status < 300) || (this.status == 304)){
539 //console.log("NAK");
541 alert(this.responseText);
551 function createCanvas(id, width, height, x, y, z, parent)
554 //width * heightの大きさのCanvasを
556 //parentには、Canvasタグを包含することになるDOMオブジェクトのidを指定する。
557 canvas = document.createElement("canvas");
558 parent = document.getElementById(parent);
562 parent.appendChild(canvas);
564 canvas.style.position = "absolute";
565 canvas.style.top = y + "px";
566 canvas.style.left = x + "px";
567 canvas.style.zIndex = z;
569 canvas.width = width;
570 canvas.height = height;
575 function createCanvas(id, width, height, x, y, z, parent)
578 //width * heightの大きさのCanvasを
580 //parentには、Canvasタグを包含することになるDOMオブジェクトのidを指定する。
581 canvas = document.createElement("canvas");
582 parent = document.getElementById(parent);
586 parent.appendChild(canvas);
588 canvas.style.position = "absolute";
589 canvas.style.top = y + "px";
590 canvas.style.left = x + "px";
591 canvas.style.zIndex = z;
593 canvas.width = width;
594 canvas.height = height;
599 function createDOMObject(typestr, idstr, parentidstr)
601 dobj = document.createElement(typestr);
602 parentObj = document.getElementById(parentidstr);
605 parentObj.appendChild(dobj);
610 function destroyDOMObjectByID(id)
612 //識別名idのDOMオブジェクトを破棄する。
613 object = document.getElementById(id);
614 parentObj = object.parentNode;
616 parentObj.removeChild(object);
619 function removeObjectFromArray(anArray, anObject)
621 //anArray中にある全てのanObjectを削除し、空いた部分は前につめる。
622 for(i = 0; i < anArray.length; i++){
623 if(anArray[i] == anObject){
624 anArray.splice(i, 1);
632 //イベントリスナーにおけるthisは、イベントリスナーを登録したオブジェクトになり、通常とは異なるので注意。
634 function keyDownEventListener(event)
636 keyDownEventListener.manager.keyDown(event);
639 function keyUpEventListener(event)
641 keyUpEventListener.manager.keyUp(event);
642 event.preventDefault();
645 function timerTickEventListener(event)
647 timerTickEventListener.manager.timerTick(event);
650 function timeStampTimerTickEventListener(event)
652 timerTickEventListener.manager.timeStampTimerTick(event);
659 function drawText(gcontext, text, x, y)
662 //前景をstrokeStyleで塗りつぶした文字列を描画する
664 //fillTextの座標は文字列の左下!
665 textsize = gcontext.measureText(text);
666 gcontext.fillRect(x, y, textsize.width, 20);
668 gcontext.fillStyle = gcontext.strokeStyle;
669 gcontext.fillText(text, x, y + 20 - 1);
673 function drawArcDegree(gcontext, radius, startAngleDegree, endAngleDegree, x, y, anticlockwise)
675 //半径radius, 中心座標(x,y)の円弧の、
676 //startAngleDegreeからendAngleDegreeまでの範囲を、
677 //(!anticlockwise)=時計回り
678 //(anticlockwise) =反時計回り
681 startAngleRadian = startAngleDegree * Math.PI / 180;
682 endAngleRadian = endAngleDegree * Math.PI / 180;
684 gcontext.beginPath();
685 gcontext.arc(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(radius), startAngleRadian, endAngleRadian, anticlockwise);
688 gcontext.closePath();
691 function fillRect(gContext, x, y, w, h)
693 gContext.fillRect(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(w), drawCoordinatesInInteger(h));
696 function strokeRect(gContext, x, y, w, h)
698 gContext.strokeRect(drawCoordinatesInInteger(x), drawCoordinatesInInteger(y), drawCoordinatesInInteger(w), drawCoordinatesInInteger(h));
701 function drawCoordinatesInInteger(coordinateElement)
703 //from http://www.html5rocks.com/ja/tutorials/canvas/performance/
704 // With a bitwise or.
705 return ((0.5 + coordinateElement) | 0);