实现游戏开始/游戏结束/重启步骤到基本的CSS画布游戏

时间:2018-03-29 06:46:12

标签: javascript css canvas html5-canvas

所以我正在尝试创建一个基本的CSS游戏。我能够让它在没有任何额外屏幕的情况下工作(游戏只是在它打开的HTML打开的那一刻开始),现在我正在尝试添加其他功能(画布中的第一个屏幕会有一个按钮)开始游戏并在游戏结束后,画布上会出现一个新的屏幕,其中包含“再试一次”按钮。

我认为我可以通过将游戏板(曾经在全局范围内/在任何函数之外)初始化为“GameStart()”函数来实现,但由于某种原因它不能正常工作。

代码正在调用我的GameStart()函数(通过在关键点放置警报来测试它),但是我无法让它调用我的draw()函数来启动实际的游戏和循环。

这是我的代码,虽然我冒昧地不包括与游戏如何工作相关的功能(draw()使用的功能)因为它们在这里只是作为垃圾邮件。

编辑: 为了澄清,我想问的是,是否可以通过这种方式为画布游戏合并不同的屏幕,或者是否存在与此完全不同的“正确”方式?

<script>

var canvas = document.getElementById("Canvas");
var ctx = canvas. getContext("2d");


function GameStart()
{
    // clear previous canvas
    context.clearRect(0, 0, canvas.width, canvas.height);
    // ball variables here
    var dx = 0;
    var dy = 0;
    var ballRadius = 7;
    var x = canvas.width/2;
    var y = canvas.height/2;

    // paddle variables here
    var paddleHeight = 10;
    var paddleWidth = 125;
    var paddleX = (canvas.width-paddleWidth)/2;
    var rightbutton = false;
    var leftbutton = false;

    document.addEventListener("keydown", keyDownHandler, false);
    document.addEventListener("keyup", keyUpHandler, false);
    document.addEventListener("mousemove", mouseMoveHandler, false);

    // destroyable blockfields here
    var brickRowCount = 6;
    var brickColumnCount = 20;
    var brickWidth = 45;
    var brickHeight = 45;
    var brickPadding = 0;
    var brickOffsetTop = 30;
    var brickOffsetLeft = 30;
    var bricks=[];

    for(c=0; c<brickColumnCount; c++) 
    {
        bricks[c] = [];
        for(r=0; r<brickRowCount; r++) 
        {
            bricks[c][r] = { x: 0, y: 0, status: 1 };
        }
    }

    // player here
    var score = 0;
    var lives = 3;
    var destroyed = 0;
    var dscore = 1;
    var endscore = 0;
    draw();
}

...这里有一堆与游戏相关的功能,但是它们不应该是问题的原因,因为当我没有尝试添加“GameStart()”函数时它们工作得很好......

function draw() {
    alert("Drawing");
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawBall();
    drawPaddle();
    collisionDetection();
    drawBricks();
    drawScore();
    drawLives();
    x += dx;
    y += dy;
    if (dy < 2 && dy >= 0)
    {
        dy += 0.05;
    }
    if (score >  0)
    {
        score=score-dscore;
    }
    if(rightbutton && paddleX < canvas.width-paddleWidth)
    {
        paddleX +=5;
    }
    else if(leftbutton && paddleX > 0)
    {
        paddleX -=5;
    }
    requestAnimationFrame(draw);
}
</script>

我对代码的唯一改变是我将之前没有任何函数内部的动作绑定到GameStart()函数中,并将draw()-call从脚本末尾移动到GameStart中()-function并添加了GameStart()函数调用到脚本的末尾

1 个答案:

答案 0 :(得分:0)

&#13;
&#13;
<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			body {
				background-color: black;
			}
			
			canvas {
				position: absolute;
				margin: auto;
				left: 0;
				right: 0;
				border: solid 1px white;
				border-radius: 10px;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas"></canvas>
		<script type="application/javascript">
		
		/*
			This here is a simple 'module pattern', it's a
			function that is immiediately invoked.
			so the variable stateManager is having the function's
			return object assigned to it.
			
			JS has a concept called 'closures'
			In a nutshell variables created inside this function
			when used by methods in stateManager won't be forgotten
			
			This effectively allows us to create what is called a namespace
			in other languages, private scope.
			
			e.g.
			
			var module = (function() {
				
				"use strict";
				This is a hint for the JS engine to enforce strict mode
				It just means to enforce better syntax rules
				
				e.g. value = 10; instead of var value = 10; will create an error.
			
				var value = 10;
			
				return {
					getValue: function() {
						return value;
					}
				};
			
			})();
			
			module.getValue(); <- returns 10
		*/
		
		// Encapsulates some simple UI for an individual state
		var UI = (function() {
		
			function UI() {
				this.labels = [];
				this.buttons = [];
				this.fontHeight = 0;
			}
			
			UI.prototype = {
				createLabel: function(labelID,labelText,x,y,FG,BG) {
					var label = {
						ID: labelID || "" + this.labels.length,
						text: labelText || "",
						x: x || 0,
						y: y || 0,
						width: 0,
						height: 0,
						FG: FG || "black",
						BG: BG || "black"
					};
					
					this.labels[labelID] = label;
					this.labels.push(label);
				},
				
				createButton: function(buttonID,buttonText,x,y,width,height,FG,BG,pressedBG,callback) {
					var button = {
						ID: buttonID || "" + this.buttons.length,
						text: buttonText || "",
						x: x || 0,
						y: y || 0,
						width: width || 0,
						height: height || 0,
						FG: FG || "black",
						BG: BG || "black",
						pressedBG: pressedBG || "black",
						callback: callback || function() {},
						isPressed: false
					};
					
					this.buttons[buttonID] = button;
					this.buttons.push(button);
				},
				
				setLabelText(labelID,newText) {
					var label = this.labels[labelID];
					
					if (label) {
						label.text = newText;
						label.width = this.ctx.measureText(newText).width + 4;
						label.height = this.fontHeight;
					}
				},
				
				setButtonText(buttonID,newText) {
					var button = this.buttons[buttonID];
					
					if (button) {
						button.text = newText;
					}
				},
				
				setFont: function(ctx,newFont,fontSize) {
					this.ctx = ctx;
					ctx.font = (fontSize|0) + "px " + (newFont || "Arial");
					this.fontHeight = fontSize;
				
					for (var i = 0; i < this.labels.length; ++i) {
						var label = this.labels[i];
						
						label.width = ctx.measureText(label.text).width + 4;
						label.height = fontSize * 1.2;
					}
				},
				
				onmousedown: function(x,y) {
					for (var i = 0; i < this.buttons.length; ++i) {
						var button = this.buttons[i];
						var isOverlapping = x > button.x && x < button.x + button.width
										 && y > button.y && y < button.y + button.height;
						
						if (isOverlapping) {
							button.isPressed = true;
							return;
						}
					}
				},
				
				onmouseup: function(x,y) {
					for (var i = 0; i < this.buttons.length; ++i) {
						var button = this.buttons[i];
						var isOverlapping = x > button.x && x < button.x + button.width
										 && y > button.y && y < button.y + button.height;
						
						if (isOverlapping && button.isPressed) {
							button.isPressed = false;
							button.callback();
							return;
						}
						
						button.isPressed = false;
					}
				},
				
				render: function(ctx) {
					for (var i = 0; i < this.labels.length; ++i) {
						var label = this.labels[i];
						
						ctx.fillStyle = label.BG;
						ctx.fillRect(
							label.x - label.width * 0.5,
							label.y - label.height  * 0.5 - this.fontHeight * 0.25,
							label.width,
							label.height
						);
						
						ctx.fillStyle = label.FG;
						ctx.fillText(label.text,label.x,label.y);
					}
					
					for (var i = 0; i < this.buttons.length; ++i) {
						var button = this.buttons[i];
						
						ctx.fillStyle = button.isPressed ? button.pressedBG : button.BG;
						ctx.fillRect(
							button.x,
							button.y,
							button.width,
							button.height
						);
						
						ctx.fillStyle = button.FG;
						ctx.fillText(
							button.text,
							button.x + button.width * 0.5,
							button.y + button.height * 0.5 + this.fontHeight * 0.25
						);
					}
				}
			};
			
			return UI;
		
		}());
		
		// Use to create and switch between different game screens
		var state = (function() {
			
			"use strict";
			
			var bgColour = "#444444";
			var canvasWidth = 0;
			var canvasHeight = 0;
			var canvas = null;
			var ctx = null;
			var bounds = null;
			
			var hasStarted = false;
			var currentState = null;
			var stateMap = {}; // Stores all states
			var stateStack = []; // Holds trace of states that have been pushed on each other
		
			function onmousemoved(e) {			
				var x = e.clientX - bounds.left;
				var y = e.clientY - bounds.top;
			
				if (currentState.onmousemoved) {
					currentState.onmousemoved(x,y);
				}
				
				if (e.button === 1 && currentState.ui) {
					ui.onmousedown(x,y);
				}
			}
		
			function onmousedown(e) {
				var x = e.clientX - bounds.left;
				var y = e.clientY - bounds.top;
				var b = e.button;
				
				if (currentState.onmousedown) {
					currentState.onmousedown(x,y,b);
				}
				
				if (currentState.ui) {
					currentState.ui.onmousedown(x,y);
				}
			}
			
			function onmouseup(e) {
				var x = e.clientX - bounds.left;
				var y = e.clientY - bounds.top;
				var b = e.button;
				
				if (currentState.onmouseup) {
					currentState.onmousemoved(x,y,b);
				}
				
				if (currentState.ui) {
					currentState.ui.onmouseup(x,y);
				}
			}
		
			function onkeydown(e) {
				if (currentState.onkeydown) {
					currentState.onkeydown(e);
				}
			}
			
			function onkeyup(e) {
				if (currentState.onkeyup) {
					currentState.onkeyup(e);
				}
			}
			
			function loop() {
				if (currentState.ontick) {
					currentState.ontick();
				}
				
				ctx.fillStyle = bgColour;
				ctx.fillRect(0,0,canvasWidth,canvasHeight);
				
				for (var i = 0; i < stateStack.length; ++i) {
					var state = stateStack[i];
					
					if (state.onrender) {
						state.onrender(ctx);
					}
					
					if (state.ui) {
						state.ui.render(ctx);
					}
				}
				
				requestAnimationFrame(loop);
			}
		
			function start() {
				for (var stateID in stateMap) {
					var state = stateMap[stateID];
					
					if (state.oncreate) {	
						state.oncreate();
						
						if (state.ui) {
							state.ui.setFont(ctx,"Courier New",14);
						}
					}
				}
				
				addEventListener("mousemoved",onmousemoved);
				addEventListener("mousedown",onmousedown);
				addEventListener("mouseup",onmouseup);
				addEventListener("keydown",onkeydown);
				addEventListener("keyup",onkeyup);
			
				requestAnimationFrame(loop);
			}
		
			return {
				setCanvas: function(_canvas,_width,_height) {
					canvas = _canvas;
					canvasWidth = _width;
					canvasHeight = _height;
					
					canvas.width = canvasWidth;
					canvas.height = canvasHeight;
					ctx = canvas.getContext("2d");
					ctx.textAlign = "center";
					bounds = canvas.getBoundingClientRect();
				},
				
				setBackground: function(r,g,b) {
					bgColour = "#" + (r|0).toString(16) + (g|0).toString(16) + (b|0).toString(16);
				},
			
				// Add a new state
				define: function(stateID,newState) {
					stateMap[stateID] = newState;
				},
				
				// Manually switch the current state (clears the stack)
				switch: function(stateID) {
					var state = stateMap[stateID];
					
					if (state) {
						for (var i = 0; i < stateStack.length; ++i) {
							var stackedState = stateStack[i];
							
							if (stackedState.onend) {
								stackedState.onend();
							}
						}
					
						stateStack.length = 1;
						stateStack[0] = state;
						currentState = state;
						
						if (currentState.onstart) {
							currentState.onstart();
						}
						
						if (!hasStarted) {
							hasStarted = true;
							start();
						}
					}
				},
				
				// add/remove from the state stack
				push: function(stateID) {
					var state = stateMap[stateID];
					
					if (state && stateStack.indexOf(state) === -1) {
						stateStack.push(state);
						
						if (hasStarted && currentState.onpause) {
							currentState.onpause();
						}
						
						currentState = stateStack[stateStack.length - 1];
						
						if (currentState.onstart) {
							currentState.onstart();
						}
						
						if (!hasStarted) {
							hasStarted = true;
							start();
						}
					}
				},
				
				pop: function() {
					if (stateStack.length > 1) {
						stateStack.pop();
						
						if (currentState.onend) {
							currentState.onend();
						}
						
						currentState = stateStack[stateStack.length - 1];
						
						if (currentState.onresume) {
							currentState.onresume();
						}
					}
				}
			};
		
		})();
		
		</script>
		
		<script type="application/javascript">
		
			/*
				This is similar to the module pattern above but
				it's strictly being used to provide a private scope
			*/
			void function() {
			
				"use strict";
				
				var canvasWidth = 180;
				var canvasHeight = 160;
				
				// Define game screens
				state.define("mainMenu",{
					oncreate: function() {
						var ui = new UI();
						
						ui.createLabel(
							"mainMenuLabel", // id
							"Main Menu", // text
							90, // x
							20, // y
							"white", // text colour
							"darkgreen" // background colour
						);
						
						ui.createButton(
							"startGameButton", // id
							"Start Game", // text
							45, // x
							55, // y
							90, // width
							30, // height
							"white", // text colour
							"#222222", // background colour
							"#555555", // background colour (when clicked on)
							function() { // function to run when clicked
								state.switch("gameState");
							}
						);
						
						ui.createButton(
							"openOptionsButton",
							"Options",
							45,
							95,
							90,
							30,
							"white",
							"#222222",
							"#555555",
							function() {
								state.switch("optionsMenu");
							}
						);
						
						this.ui = ui;
					},
				});
				
				var editingKey = '';
				var upControl = 'w';
				var downControl = 's';
				var leftControl = 'a';
				var rightControl = 'd';
				
				state.define("optionsMenu",{
					oncreate: function() {
						var ui = new UI();
						
						ui.createLabel(
							"optionsMenuLabel",
							"Options Menu",
							90,
							20,
							"white",
							"darkgreen"
						);
						
						ui.createButton(
							"upButton",
							"Up: '" + upControl + "'",
							45,
							30,
							90,
							20,
							"white",
							"#222222",
							"#555555",
							function() {
								editingKey = "up";
								ui.setButtonText("upButton","Up: '?'");
								ui.setButtonText("downButton","Down: '" + downControl + "'");
								ui.setButtonText("leftButton","Left: '" + leftControl + "'");
								ui.setButtonText("rightButton","Right: '" + rightControl + "'");
							}
						);
						
						ui.createButton(
							"downButton",
							"Down: '" + downControl + "'",
							45,
							55,
							90,
							20,
							"white",
							"#222222",
							"#555555",
							function() {
								editingKey = "down";
								ui.setButtonText("upButton","Up: '" + upControl + "'");
								ui.setButtonText("downButton","Down: '?'");
								ui.setButtonText("leftButton","Left: '" + leftControl + "'");
								ui.setButtonText("rightButton","Right: '" + rightControl + "'");
							}
						);
						
						ui.createButton(
							"leftButton",
							"Left: '" + leftControl + "'",
							45,
							80,
							90,
							20,
							"white",
							"#222222",
							"#555555",
							function() {
								editingKey = "left";
								ui.setButtonText("upButton","Up: '" + upControl + "'");
								ui.setButtonText("downButton","Down: '" + downControl + "'");
								ui.setButtonText("leftButton","Left: '?'");
								ui.setButtonText("rightButton","Right: '" + rightControl + "'");
							}
						);
						
						ui.createButton(
							"rightButton",
							"Right: '" + rightControl + "'",
							45,
							105,
							90,
							20,
							"white",
							"#222222",
							"#555555",
							function() {
								editingKey = "right";
								ui.setButtonText("upButton","Up: '" + upControl + "'");
								ui.setButtonText("downButton","Down: '" + downControl + "'");
								ui.setButtonText("leftButton","Left: '" + leftControl + "'");
								ui.setButtonText("rightButton","Right: '?'");
							}
						);
						
						ui.createButton(
							"backButton",
							"Back",
							68.5,
							135,
							45,
							15,
							"white",
							"#222222",
							"#555555",
							function() {
								ui.setButtonText("upButton","Up: '" + upControl + "'");
								ui.setButtonText("downButton","Down: '" + downControl + "'");
								ui.setButtonText("leftButton","Left: '" + leftControl + "'");
								ui.setButtonText("rightButton","Right: '" + rightControl + "'");
								state.switch("mainMenu");
							}
						);
						
						this.ui = ui;
					},
					
					onkeydown: function(e) {
						switch(editingKey) {
							case "up": {
								editingKey = "";
								upControl = e.key;
								this.ui.setButtonText("upButton","Up: '" + upControl + "'");
							} break;
							
							case "down": {
								editingKey = "";
								downControl = e.key;
								this.ui.setButtonText("downButton","Down: '" + downControl + "'");
							} break;
							
							case "left": {
								editingKey = "";
								leftControl = e.key;
								this.ui.setButtonText("leftButton","Left: '" + leftControl + "'");
							} break;
							
							case "right": {
								editingKey = "";
								rightControl = e.key;
								this.ui.setButtonText("rightButton","Right: '" + rightControl + "'");
							} break;
						}
					}
				});
				
				var player = {
					x: 90,
					y: 80,
					speed: 1.0,
					radius: 10,
					
					up: false,
					down: false,
					left: false,
					right: false,
					
					tick: function() {
						if (this.up) { this.y -= this.speed; }
						if (this.down) { this.y += this.speed; }
						if (this.left) { this.x -= this.speed; }
						if (this.right) { this.x += this.speed; }
					},
					
					render: function(ctx) {
						ctx.fillStyle = "darkred";
						ctx.strokeStyle = "black";
						ctx.lineWidth = 3;
						ctx.beginPath();
						ctx.arc(this.x,this.y,this.radius,0.0,2.0*Math.PI,false);
						ctx.fill();
						ctx.stroke();
					}
				};
				
				state.define("gameState",{
					oncreate: function() {
						var ui = new UI();
						
						ui.createButton(
							"backButton",
							"Quit",
							canvasWidth - 55,
							10,
							45,
							15,
							"white",
							"#222222",
							"#555555",
							function() {
								state.switch("mainMenu");
							}
						);
						
						this.ui = ui;
					},
					
					onkeydown: function(e) {
						switch(e.key) {
							case upControl: player.up = true; break;
							case downControl: player.down = true; break;
							case leftControl: player.left = true; break;
							case rightControl: player.right = true; break;
						}
					},
					
					onkeyup: function(e) {
						switch(e.key) {
							case upControl: player.up = false; break;
							case downControl: player.down = false; break;
							case leftControl: player.left = false; break;
							case rightControl: player.right = false; break;
						}
					},
					
					ontick: function() {
						player.tick();
					},
					
					onrender: function(ctx) {
						ctx.fillStyle = "darkblue";
						ctx.fillRect(0,0,canvasWidth,canvasHeight);
						
						player.render(ctx);
					}
				});
				
				/*
					This function runs when the page
					finishes loading, it isn't essential
					but I like to use it as a 'main method'/entry point
				*/
				window.onload = function() {
					state.setCanvas(document.getElementById("canvas"),canvasWidth,canvasHeight);
					state.switch("mainMenu");
				}
			
			}();
		
		</script>
	</body>
</html>
&#13;
&#13;
&#13;