I'm trying to teach myself no frills javascript game development. I've chosen to hold all possible places on the board for the game to need to render an x or o as possible moves in the logic object. I can't figure out how to draw the x inside the area of the rect it's to appear in. I want the player to eventually click or touch any space in the area of on of the possible moves object's rects. How do I do that? How do I redo this when I need to make them instances without any idea where the player will click or touch?
// 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>
Basic Rendering and IO
It is the basics of game development that you have assets that are rendered many times, in a variety of places, scales, orientations, etc.
Rendering
So lets start with drawing a basic cross (X) and assuming you have the 2D canvas context as
ctx
First set up the context
Then add some path elements, we will set the cross to be in a square 100 by 100 pixels.
And it is much the same for the circle
We want to make the cross and circle an entity we can draw anywhere so we will wrap each in a function definition, adding some arguments to set where and some extra details like colour.
Game state
Now we want to create some means of storing the gameboard. We can use a simple array with one items for each of the 9 areas. Also some constants to define what is held in each location
Now a function to let us set a location. We do not want this function to just blindly set a location. It will first check if it is empty and if so only then add the move. It will return true for a valid move or false if not. That makes it easy for us to add moves without having to check the board elsewhere for valid moves.
So now we can put these parts together to render the board
IO the mouse wrangler
Now we have all the rendering set up we need some input so create some mouse listeners.
Putting it all together
To ensure we don't get in the way of the DOM we need to sync our rendering with it. To do this we create a timed rendering loop. Though we well not render everything each time we can just keep it happening 60 times a second for convenience.
Snippet
As a snippet with code to add the canvas.
Hope that helps..