extend canvas to class, added human click

This commit is contained in:
Eunchong Kim 2021-07-22 21:21:13 +09:00
parent be5a129b14
commit d3ee297812
8 changed files with 230 additions and 137 deletions

View File

@ -19,37 +19,24 @@ document.addEventListener('DOMContentLoaded', () => {
global.uno_game_div = document.getElementById('uno-game'); global.uno_game_div = document.getElementById('uno-game');
global.uno_game_div.classList.add('uno-game-div'); global.uno_game_div.classList.add('uno-game-div');
// Create canvas // Create background canv
const basic_canvas = new BasicCanvas(); const bkg = new BasicCanvas(0, 0, global.uno_game_w, global.uno_game_h);
bkg.canvas.classList.add('uno-game-canv-bkg');
// Set canvas // Create room button
basic_canvas.canvas.classList.add('uno-game-canv-main'); const create_room_box_text = new BoxText(global.uno_game_w*5/12, global.uno_game_h*3/4,
global.uno_game_w/6, global.uno_game_w/12, 'Create Game');
// Add game start button with bitmap
const btn_width = global.uno_game_w/8;
const start_game_box_text = new BoxText(basic_canvas.ctx, global.uno_game_w*5/12, global.uno_game_h*3/4,
global.uno_game_w/7, global.uno_game_w/8/1.6, 'Start Game');
/* Canvas click */ /* Canvas click */
basic_canvas.canvas.addEventListener("click", e => { create_room_box_text.canvas.addEventListener("click", e => {
const rect = basic_canvas.canvas.getBoundingClientRect(); create_room_box_text.remove();
const point = { createRoom();
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
console.log(point);
if ( start_game_box_text.isClicked(point) ) {
basic_canvas.clear();
startGame();
}
}); });
}); });
/* Game start */ /* Game start */
function startGame() { async function createRoom() {
console.info('Game start'); console.info('Game start');
const room = new Room('room1'); const room = new Room('room1');
@ -59,7 +46,12 @@ function startGame() {
room.addBot(); room.addBot();
room.addBot(); room.addBot();
room.startGame(); await( room.initCards() );
//setTimeout( ()=>{ room.dealCards(); }, 2000 );
await( room.dealCards() );
await( room.startGame() );
} }

View File

@ -1,13 +1,25 @@
export default class BasicCanvas { export default class BasicCanvas {
constructor() { constructor(x, y, w, h) {
this._x = x;
this._y = y;
this._w = w;
this._h = h;
// Create canvas
this._canvas = document.createElement("CANVAS"); this._canvas = document.createElement("CANVAS");
global.uno_game_div.appendChild( this._canvas ); this._canvas.width = w;
global.canvas_count++; this._canvas.height = h;
console.debug(global.canvas_count);
this._canvas.width = global.uno_game_w;
this._canvas.height = global.uno_game_h;
this._canvas.classList.add('uno-game-canv-default'); this._canvas.classList.add('uno-game-canv-default');
this._canvas.style += 'z-index: ' + global.canvas_count + ';';
// Canvas style
this._z_index = global.canvas_count;
global.canvas_count++;
this._canvas.style = `left: ${x}px; top: ${y}px; z-index: ${this._z_index};`;
// Add to div
global.uno_game_div.appendChild( this._canvas );
// Get ctx
this._ctx = this._canvas.getContext('2d'); this._ctx = this._canvas.getContext('2d');
} }
@ -28,4 +40,18 @@ export default class BasicCanvas {
clear() { clear() {
this._ctx.clearRect(0, 0, global.uno_game_w, global.uno_game_h); this._ctx.clearRect(0, 0, global.uno_game_w, global.uno_game_h);
} }
move(x, y) {
this._canvas.style.left = x + 'px';
this._canvas.style.top = y + 'px';
}
refresh() {
this._canvas.style.zIndex = global.canvas_count;
global.canvas_count++;
}
remove() {
this._canvas.remove();
}
} }

View File

@ -1,24 +1,18 @@
export default class BoxText { import BasicCanvas from './basic_canvas.js';
constructor(ctx, x, y, w, h, text) {
this._ctx = ctx; export default class BoxText extends BasicCanvas {
this._x = x; constructor(x, y, w, h, text) {
this._y = y; super(x, y, w, h);
this._w = w;
this._h = h;
this._text = text; this._text = text;
ctx.lineWidth = 4; this._ctx.lineWidth = 4;
ctx.fillStyle = "#abc"; this._ctx.fillStyle = "#abc";
ctx.fillRect(x, y, w, h); this._ctx.fillRect(0, 0, w, h);
ctx.font = Math.floor(h/3)+"px Arial"; this._ctx.font = Math.floor(h/3)+"px Arial";
ctx.textAlign="center"; this._ctx.textAlign="center";
ctx.textBaseline = "middle"; this._ctx.textBaseline = "middle";
ctx.fillStyle = "#000000"; this._ctx.fillStyle = "#000000";
ctx.fillText(text, x+w/2, y+h/2); this._ctx.fillText(text, w/2, h/2);
}
isClicked(point) {
return ( (this._x <= point.x && point.x <= this._x + this._w)
&& (this._y <= point.y && point.y <= this._y + this._h) )
} }
} }

View File

@ -4,19 +4,32 @@ import cards_img from '../images/cards.svg';
import card_back from '../images/card_back.svg'; import card_back from '../images/card_back.svg';
export default class Card extends BasicCanvas { export default class Card extends BasicCanvas {
constructor(num, color_n) { constructor(x, y, num, color_n) {
super(); super(x, y, global.uno_game_w/16, global.uno_game_w/16*360/240);
this._num = num;
this._color_n = color_n;
this._c_w = 240; this._c_w = 240;
this._c_h = 360; this._c_h = 360;
this._num = num;
this._color_n = color_n;
this._event_is_set = false;
this._cards_img = new Image(); this._cards_img = new Image();
this._cards_img.src = cards_img; this._cards_img.src = cards_img;
this._card_back_img = new Image(); this._card_back_img = new Image();
this._card_back_img.src = card_back; this._card_back_img.src = card_back;
// Add border
this._canvas.style.border = '1px solid #000000';
// Add transition
this._canvas.style.transition = '0.5s';
// Fill
this._card_back_img.addEventListener('load', () => {
this._ctx.drawImage(this._card_back_img, 0, 0, this._w, this._h);
this._is_front = false;
});
} }
get num() { get num() {
@ -32,6 +45,13 @@ export default class Card extends BasicCanvas {
this._color_n = color_n; this._color_n = color_n;
} }
get event_is_set() {
return this._event_is_set;
}
set event_is_set(event_is_set) {
this._event_is_set = event_is_set;
}
isMatch(card) { isMatch(card) {
if ( (this._num <= 12 && this._num === card.num) if ( (this._num <= 12 && this._num === card.num)
|| (this._num >= 13) || (this._num >= 13)
@ -42,22 +62,33 @@ export default class Card extends BasicCanvas {
} }
} }
drawImageFront(x, y) { flip() {
this.clear(); this.clear();
this._x = x; if (this._is_front) {
this._y = y; this._ctx.drawImage(this._card_back_img, 0, 0, this._w, this._h);
this._w = global.uno_game_w/16; this._is_front = false;
this._h = this._w * this._c_h / this._c_w; } else {
this._cards_img.addEventListener('load', () => {
this._ctx.drawImage(this._cards_img, 1+this._c_w*this._num, 1+this._c_h*this._color_n, this._c_w, this._c_h, this._ctx.drawImage(this._cards_img, 1+this._c_w*this._num, 1+this._c_h*this._color_n, this._c_w, this._c_h,
x, y, this._w, this._h); 0, 0, this._w, this._h);
this._is_front = true;
});
}
}
drawImageFront(x, y) {
this._canvas.style.left = x + 'px';
this._canvas.style.top = y + 'px';
this.clear();
this._ctx.drawImage(this._cards_img, 1+this._c_w*this._num, 1+this._c_h*this._color_n, this._c_w, this._c_h,
0, 0, this._w, this._h);
} }
drawImageBack(x, y) { drawImageBack(x, y) {
this._canvas.style.left = x + 'px';
this._canvas.style.top = y + 'px';
this.clear(); this.clear();
this._x = x; this._ctx.drawImage(this._card_back_img, 0, 0, this._w, this._h);
this._y = y;
this._w = global.uno_game_w/16;
this._h = this._w * this._c_h / this._c_w;
this._ctx.drawImage(this._card_back_img, x, y, this._w, this._h);
} }
} }

View File

@ -3,10 +3,12 @@ import Player from './player.js';
export default class Human extends Player { export default class Human extends Player {
constructor(name, id) { constructor(name, id) {
super(name, id); super(name, id);
this._type = 'human'; this._type = 'human';
} }
playCard(top_card) { playCard(top_card) {
return this._cards.splice(0, 1)[0]; return this._cards.splice(0, 1)[0];
} }
} }

View File

@ -40,9 +40,29 @@ export default class Player extends BasicCanvas {
addCard(card) { addCard(card) {
this._cards.push(card); this._cards.push(card);
this.sortCards();
if (this.type === 'human') {
card.flip();
}
}
removeCard(card) {
this._cards.splice(this._cards.indexOf(card), 1);
} }
isEmpty() { isEmpty() {
return (this._cards.length === 0) ? true : false; return (this._cards.length === 0) ? true : false;
} }
refreshCards() {
for (let i=0; i<this._cards.length; i++) {
this._cards[i].move(global.uno_game_w*(i+1)/16, global.uno_game_h*(5-this.id-1)/5);
}
}
sortCards() {
this._cards.sort( (a, b) => (a.num > b.num) ? 1 : -1 );
this.refreshCards();
}
} }

View File

@ -6,7 +6,7 @@ import Card from './card.js';
export default class Room extends BasicCanvas { export default class Room extends BasicCanvas {
constructor(name) { constructor(name) {
super(); super(0, 0, global.uno_game_w, global.uno_game_h/10);
this._name = name; this._name = name;
this._players = []; this._players = [];
@ -14,8 +14,8 @@ export default class Room extends BasicCanvas {
this._used_cards = []; this._used_cards = [];
// Fill room name // Fill room name
this._ctx.font = "32px Arial"; this._ctx.font = Math.floor(this._h/3) + "px Arial";
this._ctx.fillText(name, 10, 10); this._ctx.fillText(name, 10, 50);
} }
addHuman(name) { addHuman(name) {
@ -28,95 +28,129 @@ export default class Room extends BasicCanvas {
console.log(this._players); console.log(this._players);
} }
initDeck() { initCards() {
for (let x=0; x<14; x++) { console.log('Init')
for (let y=0; y<8; y++) { const index_arr = [...Array(108).keys()];
if ( (x === 0) && (y >= 4) ) { // Skip blank card let cnt = 0;
for (let num=0; num<14; num++) {
for (let color_n=0; color_n<8; color_n++) {
if ( (num === 0) && (color_n >= 4) ) { // Skip blank card
continue; continue;
} }
if ( (x === 13) && (y >= 4) ) { // +4 cards if ( (num === 13) && (color_n >= 4) ) { // +4 cards
x = 14; num = 14;
} }
this._cards.push( new Card(x, y%4) ); const card_index = index_arr.splice(Math.floor( Math.random() * index_arr.length), 1)[0];
this._cards[ card_index ] = new Card(global.uno_game_w*6/16+card_index, global.uno_game_h/2, num, color_n%4);
cnt++;
} }
} }
for (let i=0; i<this._cards.length; i++) {
this._cards[i].refresh();
}
console.log(this._cards); console.log(this._cards);
} }
shuffleDeck() { dealCards() {
console.log('Deal cards')
this._players.forEach( (player) => { this._players.forEach( (player) => {
for (let i=0; i<7; i++) { for (let i=0; i<7; i++) {
player.addCard( this.draw() ); player.addCard( this._cards.pop() );
} }
}); });
console.log(this._players); console.log(this._players);
} }
draw() {
/* Draw randomly */
const card_i = Math.floor( Math.random() * this._cards.length );
return this._cards.splice(card_i, 1)[0];
}
updateView() {
const inner_w = window.innerWidth;
const inner_h = window.innerHeight;
// Drw top card
this._top_card.drawImageFront(inner_w/2, inner_h/2);
// Draw players card
this._players.forEach( (player) => {
for (let i=0; i<player.cards.length; i++) {
const card = player.cards[i];
card.clear();
if (player.type == 'bot') {
card.drawImageBack(inner_w*(i+1)/16, inner_h*player.id/5);
} else {
card.drawImageFront(inner_w*(i+1)/16, inner_h*4/5);
}
}
});
}
async startGame() { async startGame() {
await( this.initDeck() );
await( this.shuffleDeck() );
let count = 0;
let current_turn = 0;
this._top_card = this.draw();
console.log('Game start'); console.log('Game start');
while (true) { this._current_player = 0;
console.log('count: ' + count + ', current player: ' + this._players[current_turn].name); this._turn_count = 0;
const player = this._players[current_turn];
const card = await( player.playCard(this._top_card) ); this._top_card = this._cards.pop();
this._top_card.flip();
this._top_card.move(global.uno_game_w*8/16+this._turn_count, global.uno_game_h/2);
//this._top_card.drawImageFront(global.uno_game_w*8/16+this._turn_count, global.uno_game_h/2);
this.humanTurn(this._players[this._current_player]);
// if (player.isEmpty()) {
// console.log('player: ' + player.name + ' has no card left. Game end');
// break
// }
}
async botPlay(player) {
console.log('Turn count: ' + this._turn_count + ', current player: ' + player.name);
await new Promise(resolve => setTimeout(resolve, 1000));
const card = player.playCard(this._top_card);
if (card) { if (card) {
console.log('player: ' + player.name + ' played card num: ' + card.num + ', color: ' + card.color_n); console.log('player: ' + player.name + ' played card num: ' + card.num + ', color: ' + card.color_n);
card.clear(); this.changeTopCard(card);
this._used_cards.push(this._top_card);
this._top_card = card;
} else { } else {
const card = this.draw(); const card = this._cards.pop();
console.log('player: ' + player.name + ' drawed card num: ' + card.num + ', color: ' + card.color_n); console.log('player: ' + player.name + ' drawed card num: ' + card.num + ', color: ' + card.color_n);
player.addCard(card); player.addCard(card);
} }
this.updateView(); player.refreshCards();
//await Promise.all([
// timeout(1000)
//]);
await new Promise(resolve => setTimeout(resolve, 1000));
if (player.isEmpty()) { this._turn_count++;
console.log('player: ' + player.name + ' has no card left. Game end'); const next_player = this.getNextPlayer();
break if (next_player.type === 'human') {
this.humanTurn(next_player);
} else {
this.botPlay(next_player);
}
} }
current_turn = (current_turn >= this._players.length-1) ? 0 : ++current_turn; humanTurn(player) {
count++; player.cards.forEach( (card) => {
if (!card.event_is_set) {
card.canvas.addEventListener('click', ()=>{
this.humanPlay(player, card);
player.removeCard(card);
});
card.event_is_set = true;
} }
});
}
async humanPlay(player, card) {
console.log('Turn count: ' + this._turn_count + ', current player: ' + player.name);
this.changeTopCard(card);
console.log('player: ' + player.name + ' played card num: ' + card.num + ', color: ' + card.color_n);
player.refreshCards();
this._turn_count++;
this.botPlay( this.getNextPlayer() );
}
getNextPlayer() {
if (this._reverse) {
if (this._current_player === 0) {
this._current_player = this._players.length-1;
} else {
this._current_player--;
}
} else {
if (this._current_player === this._players.length-1) {
this._current_player = 0;
} else {
this._current_player++;
}
}
return this._players[this._current_player];
}
changeTopCard(card) {
this._used_cards.push(this._top_card);
this._top_card = card;
this._top_card.drawImageFront(global.uno_game_w*8/16+this._turn_count, global.uno_game_h/2);
this._top_card.refresh();
} }
} }

View File

@ -7,14 +7,8 @@
.uno-game-canv-default { .uno-game-canv-default {
position: absolute; position: absolute;
display: block; display: block;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
} }
.uno-game-canv-main { .uno-game-canv-bkg {
background-image: url('../images/green_table.jpg'); background-image: url('../images/green_table.jpg');
} }