我正在努力教自己没有多余的javascript游戏开发。我选择在游戏板上保留所有可能的位置,以便游戏中需要在逻辑对象中渲染x或o作为可能的移动。我无法弄清楚如何在它出现的矩形区域内绘制x。我希望玩家最终点击或触摸可能移动对象的rects区域中的任何空间。我怎么做?如果我需要在不知道玩家点击或触摸的位置的情况下制作实例,我该如何重做呢?
// the stage object holds the HTML5 canvas, it's 2d context, and a self starting function that sizes it. (unless all ready fired, canvas is not defined.)
var stage = {
canvas: document.getElementById('canvas'),
context: this.canvas.getContext('2d'),
full_screen: (function () {
this.canvas.width = document.documentElement.clientWidth;
this.canvas.height = window.innerHeight;
this.canvas.style.border = '1px solid black';
console.log(this.canvas);
return this.canvas;
})()
};
stage.width = stage.canvas.width;
stage.height = stage.canvas.height;
var init = function () {
// ui for the game
var button = {
pause: document.getElementById('pause'),
restart: document.getElementById('restart'),
options: document.getElementById('opt')
};
// this function assigns functions the ui buttons
var functionality = function () {
button.pause.onclick = pause;
button.restart.onclick = restart;
button.options.onclick = options;
};
var logic = {
player: { score: 0 },
cpu: { score: 0 },
possible_moves: {
x: 0,
y: 0,
top_left: {
x: stage.width * .05,
y: stage.height * .02,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
top_middle: {
x: stage.canvas.width * .385,
y: stage.canvas.height * .02,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
top_right: {
x: stage.canvas.width * .715,
y: stage.canvas.height * .02,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
middle_left: {
x: stage.canvas.width * .05,
y: stage.canvas.height * .35,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
middle_middle: {
x: stage.canvas.width * .385,
y: stage.canvas.height * .35,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
middle_right: {
x: stage.canvas.width * .715,
y: stage.canvas.height * .35,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
bottom_left: {
x: stage.canvas.width * .05,
y: stage.canvas.height * .68,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
bottom_middle: {
x: stage.canvas.width * .385,
y: stage.canvas.height * .68,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
bottom_right: {
x: stage.canvas.width * .715,
y: stage.canvas.height * .68,
width: stage.width * .22,
height: stage.height * .22,
draw: function () {
stage.context.beginPath();
stage.context.lineWidth = 1;
stage.context.rect(this.x, this.y, this.width, this.height);
stage.context.stroke();
}
},
draw_top_row: function () {
logic.possible_moves.top_left.draw();
logic.possible_moves.top_middle.draw();
logic.possible_moves.top_right.draw();
},
draw_middle_row: function () {
logic.possible_moves.middle_left.draw();
logic.possible_moves.middle_middle.draw();
logic.possible_moves.middle_right.draw();
},
draw_bottom_row: function () {
logic.possible_moves.bottom_left.draw();
logic.possible_moves.bottom_middle.draw();
logic.possible_moves.bottom_right.draw();
},
draw_left_column: function () {
logic.possible_moves.top_left.draw();
logic.possible_moves.middle_left.draw();
logic.possible_moves.bottom_left.draw();
},
draw_middle_column: function () {
logic.possible_moves.top_middle.draw();
logic.possible_moves.middle_middle.draw();
logic.possible_moves.bottom_middle.draw();
},
draw_right_column: function () {
logic.possible_moves.top_right.draw();
logic.possible_moves.middle_right.draw();
logic.possible_moves.bottom_right.draw();
},
draw_left_to_right_diagonal: function () {
logic.possible_moves.top_left.draw();
logic.possible_moves.middle_middle.draw();
logic.possible_moves.bottom_right.draw();
},
draw_right_to_left_diagonal: function () {
logic.possible_moves.top_right.draw();
logic.possible_moves.middle_middle.draw();
logic.possible_moves.bottom_left.draw();
},
draw_all_moves: function () {
logic.possible_moves.top_left.draw();
logic.possible_moves.top_middle.draw();
logic.possible_moves.top_right.draw();
logic.possible_moves.middle_left.draw();
logic.possible_moves.middle_middle.draw();
logic.possible_moves.middle_right.draw();
logic.possible_moves.bottom_left.draw();
logic.possible_moves.bottom_middle.draw();
logic.possible_moves.bottom_right.draw();
},
generate_logic_map: (function () {
})()
}
};
// I had to add the scoreboard to the logic object as an after thought because I wanted to just reference the two individual player and cpu objects in case I need to increase complextity to those cbjects seperately. Also, jaascript won't allow me to reference these propties "inside" the object.
logic.score_board = {
p: logic.player.score,
c: logic.cpu.score
};
// this object holds the visual elements of the game
var assets = {
x: {
left_to_right: {
x1: logic.possible_moves.top_left.x,
y1: logic.possible_moves.top_left.y,
x2: logic.possible_moves.top_left.width,
y2: logic.possible_moves.top_left.height,
draw: function () {
stage.context.beginPath();
stage.context.moveTo(this.x1, this.y1);
stage.context.lineTo(this.x2, this.y2);
stage.context.stroke();
console.log(this.x1, this.x2, this.y1, this.y2);
}
},
right_to_left: {
x1: logic.possible_moves.top_left.width,
y1: logic.possible_moves.top_left.height,
x2: 0,
y2: 43,
draw: function () {
stage.context.beginPath();
stage.context.moveTo(this.x1, this.y1);
stage.context.lineTo(this.x2, this.y2);
stage.context.stroke();
console.log(this.x1, this.x2, this.y1, this.y2);
}
},
draw: function () {
console.log(this.left_to_right.x1, this.left_to_right.y1, this.left_to_right.x2, this.left_to_right.y2);
stage.context.lineWidth = 5;
stage.context.strokeStyle = 'black';
this.left_to_right.draw();
//this.right_to_left.draw();
}
},
o: {},
grid: {
x: 0,
y: 0,
horizontal_line_l: {
x1: stage.canvas.width * .02,
y1: stage.canvas.height * .33,
x2: stage.canvas.width * .98,
y2: stage.canvas.height * .33,
draw: function () {
stage.context.beginPath();
stage.context.moveTo(this.x1, this.y1);
stage.context.lineTo(this.x2, this.y2);
stage.context.stroke();
}
},
horizontal_line_r: {
x1: stage.canvas.width * .02,
y1: stage.canvas.height * .66,
x2: stage.canvas.width * .98,
y2: stage.canvas.height * .66,
draw: function () {
stage.context.beginPath();
stage.context.moveTo(this.x1, this.y1);
stage.context.lineTo(this.x2, this.y2);
stage.context.stroke();
}
},
vertical_line_u: {
x1: stage.canvas.width * .33,
y1: stage.canvas.height * .02,
x2: stage.canvas.width * .33,
y2: stage.canvas.height * .98,
draw: function () {
stage.context.beginPath();
stage.context.moveTo(this.x1, this.y1);
stage.context.lineTo(this.x2, this.y2);
stage.context.stroke();
}
},
vertical_line_d: {
x1: stage.canvas.width * .66,
y1: stage.canvas.height * .02,
x2: stage.canvas.width * .66,
y2: stage.canvas.height * .98,
draw: function () {
stage.context.beginPath();
stage.context.moveTo(this.x1, this.y1);
stage.context.lineTo(this.x2, this.y2);
stage.context.stroke();
}
},
draw: function () {
stage.context.lineWidth = 20;
stage.context.strokeStyle = '#0000ff';
stage.context.lineCap = 'round';
this.horizontal_line_l.draw();
this.horizontal_line_r.draw();
this.vertical_line_u.draw();
this.vertical_line_d.draw();
}
},
text: {}
};
assets.grid.draw();
logic.possible_moves.draw_all_moves();
assets.x.draw();
};
window.onload = init();
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Tik Tack Toe</title>
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.18.1/build/cssreset/cssreset-min.css">
<link rel="stylesheet" type="text/css" href="game.css" />
</head>
<body>
<div id="container">
<canvas id="canvas"></canvas>
<div id="UI" class="">
<ul>
<li><button id="pause">Pause</button></li>
<li><button id="restart">Restart</button></li>
<li><button id="opt">Options</button></li>
</ul>
</div>
</div>
<script src="game.js"></script>
</body>
</html>
答案 0 :(得分:1)
游戏开发的基础是你拥有多次渲染的资源,包括各种场所,尺度,方向等。
所以我们先从绘制一个基本的十字架(X)开始,假设您将2D画布上下文设为ctx
首先设置上下文
ctx.strokeStyle = "black"; // the colour/style of the cross
ctx.lineWidth = 10; // the width of a stroke in pixels.
然后添加一些路径元素,我们将十字架设置为100×100像素的正方形。
// Very important that you use the following line whenever creating new paths
// if not you end up adding to the existing path
ctx.beginPath(); // tell the context we are starting a new path.
ctx.moveTo(10,10); // start of first line top left
ctx.lineTo(90,90); // create a line to the bottom right
ctx.moveTo(90,10); // move to the top right
ctx.lineTo(10,90); // create a line to the bottom left
// now the path is defined we can render it
ctx.stroke();
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
ctx.strokeStyle = "black"; // the colour/style of the cross
ctx.lineWidth = 10; // the width of a stroke in pixels.
// Very important that you use the following line whenever creating new paths
// if not you end up adding to the existing path
ctx.beginPath(); // tell the context we are starting a new path.
ctx.moveTo(10,10); // start of first line top left
ctx.lineTo(90,90); // create a line to the bottom right
ctx.moveTo(90,10); // move to the top right
ctx.lineTo(10,90); // create a line to the bottom left
// now the path is defined we can render it
ctx.stroke();
&#13;
圆圈的情况大致相同
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
ctx.strokeStyle = "black"; // the colour/style of the cross
ctx.lineWidth = 10; // the width of a stroke in pixels.
// Very important that you use the following line whenever creating new paths
// if not you end up adding to the existing path
ctx.beginPath(); // tell the context we are starting a new path.
ctx.arc(50,50,40,0,Math.PI * 2); // create a circle path
// now the path is defined we can render it
ctx.stroke();
&#13;
我们想要交叉并圈出一个我们可以在任何地方绘制的实体,所以我们将每个包装在一个函数定义中,添加一些参数来设置where和一些额外的细节,如color。
// draw a cross with the top left at x,y
function drawCross(x,y,col){
ctx.save(); // save the current canvas context state
ctx.translate(x,y); // set where on the canvas the top left will be
ctx.strokeStyle = col;
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(90,90);
ctx.moveTo(90,10);
ctx.lineTo(10,90);
ctx.stroke();
ctx.restore(); // now restore the canvas state
}
function drawCircle(x,y,col){
ctx.save(); // save the current canvas context state
ctx.translate(x,y); // set where on the canvas the top left will be
ctx.strokeStyle = col;
ctx.lineWidth = 10;
ctx.beginPath();
ctx.arc(50,50,40,0,Math.PI * 2); // create a circle path
ctx.stroke();
ctx.restore(); // now restore the canvas state
}
现在我们想要创建一些存储游戏板的方法。我们可以使用一个简单的数组,其中包含9个区域中的每个区域。还有一些常量来定义每个位置的内容
// a 2d array containing 3 arrays one for each row
var gameBoard = [[0,0,0],[0,0,0],[0,0,0]];
const empty = 0;
const cross = 1;
const circle = 2;
var turn = circle; // whos turn it is
现在是一个让我们设置位置的功能。我们不希望这个功能只是盲目地设置一个位置。它将首先检查它是否为空,如果是,则仅添加移动。对于有效的移动,它将返回true,否则返回false。这样我们就可以轻松添加动作而无需在其他地方检查其他有效动作。
// set a board position x y with a type
function setBoard(x,y,type){
if(gameBoard[y][x] === empty){ // only if empty
gameBoard[y][x] = type;
return true; // indicate we have set the position
}
return false; // could not set location
}
所以现在我们可以把这些部分放在一起来渲染电路板
function renderBoard(){
var x, y;
// as we may have some stuff already drawn we need to clear the
// board
ctx.clearRect(0,0,300,300);
// lets draw the horizontal and vertical lines
// We can use fillRect as it does not need the beginPath command
// or a line width
ctx.fillStyle = "black";
ctx.fillRect(97,0,6,300);
ctx.fillRect(197,0,6,300);
ctx.fillRect(0,97,300,6);
ctx.fillRect(0,197,300,6);
for(y = 0; y < 3; y ++){
for(x = 0; x < 3; x++){
var loc = gameBoard[y][x]; // get what is at the location
if(loc === cross){ // is it a cross?
// as the area is 100 by 100 pixels we need th correct top left
// coordinate, so multiply the x and y by 100
drawCross(x * 100, y * 100, "red");
}else if(loc === circle){ // is it a circle
drawCircle(x * 100, y * 100, "red");
}
}
}
}
现在我们已经完成了所有渲染设置,我们需要一些输入,所以创建一些鼠标监听器。
// fisrt a mouse object to hold mouse state
const mouse = {};
function mouseEvent(event){
var bounds = canvas.getBoundingClientRect(); // get the canvas loc
// get the mouse position relative to the canvas top left
mouse.x = event.pageX - (bounds.left + scrollX);
mouse.y = event.pageY - (bounds.top + scrollY);
if(event.type === "mouseup"){ // when the mouse button is up we have a click
mouse.clicked = true;
}
}
canvas.addEventListener("mousemove",mouseEvent);
canvas.addEventListener("mouseup",mouseEvent);
为了确保我们不会妨碍DOM,我们需要将渲染与其同步。为此,我们创建一个定时渲染循环。虽然为了方便起见,我们每次只能保持一次60次,但我们不会渲染所有内容。
var turn = circle; // who turn it is
function mainLoop(){
requestAnimationFrame(mainLoop); // ask the DOM for the next convenient time to render
// now check the mouse button
if(mouse.clicked){ // yes a click
mouse.clicked = false; // clear the click
// now convert the pixel coords of mouse to game board coords
var bx = Math.floor(mouse.x / 100);
var by = Math.floor(mouse.y / 100);
if(setBoard(dx,dy,turn)){ // set the location. Function returns true if a valid move
// all good so draw the board
renderBoard();
// getthe next turn
turn = turn === circle ? cross : circle;
}
}
}
// start it all going
requestAnimationFrame(mainLoop);
作为包含添加画布的代码的代码段。
const canvas = document.createElement("canvas");
canvas.width = canvas.height = 300;
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
// draw a cross with the top left at x,y
function drawCross(x,y,col){
ctx.save(); // save the current canvas context state
ctx.translate(x,y); // set where on the canvas the top left will be
ctx.strokeStyle = col;
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(90,90);
ctx.moveTo(90,10);
ctx.lineTo(10,90);
ctx.stroke();
ctx.restore(); // now restore the canvas state
}
function drawCircle(x,y,col){
ctx.save(); // save the current canvas context state
ctx.translate(x,y); // set where on the canvas the top left will be
ctx.strokeStyle = col;
ctx.lineWidth = 10;
ctx.beginPath();
ctx.arc(50,50,40,0,Math.PI * 2); // create a circle path
ctx.stroke();
ctx.restore(); // now restore the canvas state
}
// a 2d array containing 3 arrays one for each row
var gameBoard = [[0,0,0],[0,0,0],[0,0,0]];
const empty = 0;
const cross = 1;
const circle = 2;
// set a board position x y with a type
function setBoard(x,y,type){
if(gameBoard[y][x] === empty){ // only if empty
gameBoard[y][x] = type;
return true; // indicate we have set the position
}
return false; // could not set location
}
function renderBoard(){
var x, y;
// as we may have some stuff already drawn we need to clear the
// board
ctx.clearRect(0,0,300,300);
// lets draw the horizontal and vertical lines
// We can use fillRect as it does not need the beginPath command
// or a line width
ctx.fillStyle = "black";
ctx.fillRect(97,0,6,300);
ctx.fillRect(197,0,6,300);
ctx.fillRect(0,97,300,6);
ctx.fillRect(0,197,300,6);
for(y = 0; y < 3; y ++){
for(x = 0; x < 3; x++){
var loc = gameBoard[y][x]; // get what is at the location
if(loc === cross){ // is it a cross?
// as the area is 100 by 100 pixels we need th correct top left
// coordinate, so multiply the x and y by 100
drawCross(x * 100, y * 100, "red");
}else if(loc === circle){ // is it a circle
drawCircle(x * 100, y * 100, "blue");
}
}
}
}
// fisrt a mouse object to hold mouse state
const mouse = {};
function mouseEvent(event){
var bounds = canvas.getBoundingClientRect(); // get the canvas loc
// get the mouse position relative to the canvas top left
mouse.x = event.pageX - (bounds.left + scrollX);
mouse.y = event.pageY - (bounds.top + scrollY);
if(event.type === "mouseup"){ // when the mouse button is up we have a click
mouse.clicked = true;
}
}
canvas.addEventListener("mousemove",mouseEvent);
canvas.addEventListener("mouseup",mouseEvent);
var turn = circle; // who turn it is
function mainLoop(){
requestAnimationFrame(mainLoop); // ask the DOM for the next convenient time to render
// now check the mouse button
if(mouse.clicked){ // yes a click
mouse.clicked = false; // clear the click
// now convert the pixel coords of mouse to game board coords
var bx = Math.floor(mouse.x / 100);
var by = Math.floor(mouse.y / 100);
if(setBoard(bx,by,turn)){ // set the location. Function returns true if a valid move
// all good so draw the board
renderBoard();
// getthe next turn
turn = turn === circle ? cross : circle;
}
}
}
// draw the empty board
renderBoard();
// start it all going
requestAnimationFrame(mainLoop);
&#13;
希望有所帮助..