From d3ee297812162e41fb7165a4aa9d73c70930b2cc Mon Sep 17 00:00:00 2001 From: Eunchong Kim Date: Thu, 22 Jul 2021 21:21:13 +0900 Subject: [PATCH] extend canvas to class, added human click --- src/index.js | 40 ++++------ src/js/basic_canvas.js | 40 ++++++++-- src/js/box_text.js | 34 ++++----- src/js/card.js | 63 +++++++++++---- src/js/human.js | 2 + src/js/player.js | 20 +++++ src/js/room.js | 160 ++++++++++++++++++++++++--------------- src/styles/uno-game.scss | 8 +- 8 files changed, 230 insertions(+), 137 deletions(-) diff --git a/src/index.js b/src/index.js index 7a3ee27..5daf155 100644 --- a/src/index.js +++ b/src/index.js @@ -19,37 +19,24 @@ document.addEventListener('DOMContentLoaded', () => { global.uno_game_div = document.getElementById('uno-game'); global.uno_game_div.classList.add('uno-game-div'); - // Create canvas - const basic_canvas = new BasicCanvas(); + // Create background canv + const bkg = new BasicCanvas(0, 0, global.uno_game_w, global.uno_game_h); + bkg.canvas.classList.add('uno-game-canv-bkg'); - // Set canvas - basic_canvas.canvas.classList.add('uno-game-canv-main'); - - // 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'); + // Create room button + 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'); /* Canvas click */ - basic_canvas.canvas.addEventListener("click", e => { - const rect = basic_canvas.canvas.getBoundingClientRect(); - const point = { - x: e.clientX - rect.left, - y: e.clientY - rect.top - }; - - console.log(point); - if ( start_game_box_text.isClicked(point) ) { - basic_canvas.clear(); - startGame(); - } + create_room_box_text.canvas.addEventListener("click", e => { + create_room_box_text.remove(); + createRoom(); }); - }); /* Game start */ -function startGame() { +async function createRoom() { console.info('Game start'); const room = new Room('room1'); @@ -59,7 +46,12 @@ function startGame() { room.addBot(); room.addBot(); - room.startGame(); + await( room.initCards() ); + + //setTimeout( ()=>{ room.dealCards(); }, 2000 ); + await( room.dealCards() ); + + await( room.startGame() ); } diff --git a/src/js/basic_canvas.js b/src/js/basic_canvas.js index d222cb2..3604ce7 100644 --- a/src/js/basic_canvas.js +++ b/src/js/basic_canvas.js @@ -1,13 +1,25 @@ 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"); - global.uno_game_div.appendChild( this._canvas ); - global.canvas_count++; - console.debug(global.canvas_count); - this._canvas.width = global.uno_game_w; - this._canvas.height = global.uno_game_h; + this._canvas.width = w; + this._canvas.height = h; 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'); } @@ -28,4 +40,18 @@ export default class BasicCanvas { clear() { 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(); + } } diff --git a/src/js/box_text.js b/src/js/box_text.js index 0956bbf..42bd2e3 100644 --- a/src/js/box_text.js +++ b/src/js/box_text.js @@ -1,24 +1,18 @@ -export default class BoxText { - constructor(ctx, x, y, w, h, text) { - this._ctx = ctx; - this._x = x; - this._y = y; - this._w = w; - this._h = h; +import BasicCanvas from './basic_canvas.js'; + +export default class BoxText extends BasicCanvas { + constructor(x, y, w, h, text) { + super(x, y, w, h); + this._text = text; - ctx.lineWidth = 4; - ctx.fillStyle = "#abc"; - ctx.fillRect(x, y, w, h); - ctx.font = Math.floor(h/3)+"px Arial"; - ctx.textAlign="center"; - ctx.textBaseline = "middle"; - ctx.fillStyle = "#000000"; - ctx.fillText(text, x+w/2, y+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) ) + this._ctx.lineWidth = 4; + this._ctx.fillStyle = "#abc"; + this._ctx.fillRect(0, 0, w, h); + this._ctx.font = Math.floor(h/3)+"px Arial"; + this._ctx.textAlign="center"; + this._ctx.textBaseline = "middle"; + this._ctx.fillStyle = "#000000"; + this._ctx.fillText(text, w/2, h/2); } } diff --git a/src/js/card.js b/src/js/card.js index 247e95e..7b3c498 100644 --- a/src/js/card.js +++ b/src/js/card.js @@ -4,19 +4,32 @@ import cards_img from '../images/cards.svg'; import card_back from '../images/card_back.svg'; export default class Card extends BasicCanvas { - constructor(num, color_n) { - super(); - - this._num = num; - this._color_n = color_n; + constructor(x, y, num, color_n) { + super(x, y, global.uno_game_w/16, global.uno_game_w/16*360/240); this._c_w = 240; 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.src = cards_img; this._card_back_img = new Image(); 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() { @@ -32,6 +45,13 @@ export default class Card extends BasicCanvas { 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) { if ( (this._num <= 12 && this._num === card.num) || (this._num >= 13) @@ -42,22 +62,33 @@ export default class Card extends BasicCanvas { } } - drawImageFront(x, y) { + flip() { + this.clear(); + if (this._is_front) { + this._ctx.drawImage(this._card_back_img, 0, 0, this._w, this._h); + this._is_front = false; + } 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, + 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._x = x; - this._y = y; - this._w = global.uno_game_w/16; - this._h = this._w * this._c_h / this._c_w; 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); } drawImageBack(x, y) { + this._canvas.style.left = x + 'px'; + this._canvas.style.top = y + 'px'; this.clear(); - this._x = x; - 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); + this._ctx.drawImage(this._card_back_img, 0, 0, this._w, this._h); } + } diff --git a/src/js/human.js b/src/js/human.js index 760fe81..a837c51 100644 --- a/src/js/human.js +++ b/src/js/human.js @@ -3,10 +3,12 @@ import Player from './player.js'; export default class Human extends Player { constructor(name, id) { super(name, id); + this._type = 'human'; } playCard(top_card) { return this._cards.splice(0, 1)[0]; } + } diff --git a/src/js/player.js b/src/js/player.js index 08a0ebd..058d26b 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -40,9 +40,29 @@ export default class Player extends BasicCanvas { addCard(card) { this._cards.push(card); + this.sortCards(); + if (this.type === 'human') { + card.flip(); + } + } + + removeCard(card) { + this._cards.splice(this._cards.indexOf(card), 1); } isEmpty() { return (this._cards.length === 0) ? true : false; } + + refreshCards() { + for (let i=0; i (a.num > b.num) ? 1 : -1 ); + this.refreshCards(); + } + } diff --git a/src/js/room.js b/src/js/room.js index 1830007..6202df6 100644 --- a/src/js/room.js +++ b/src/js/room.js @@ -6,7 +6,7 @@ import Card from './card.js'; export default class Room extends BasicCanvas { constructor(name) { - super(); + super(0, 0, global.uno_game_w, global.uno_game_h/10); this._name = name; this._players = []; @@ -14,8 +14,8 @@ export default class Room extends BasicCanvas { this._used_cards = []; // Fill room name - this._ctx.font = "32px Arial"; - this._ctx.fillText(name, 10, 10); + this._ctx.font = Math.floor(this._h/3) + "px Arial"; + this._ctx.fillText(name, 10, 50); } addHuman(name) { @@ -28,95 +28,129 @@ export default class Room extends BasicCanvas { console.log(this._players); } - initDeck() { - for (let x=0; x<14; x++) { - for (let y=0; y<8; y++) { - if ( (x === 0) && (y >= 4) ) { // Skip blank card + initCards() { + console.log('Init') + const index_arr = [...Array(108).keys()]; + 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; } - if ( (x === 13) && (y >= 4) ) { // +4 cards - x = 14; + if ( (num === 13) && (color_n >= 4) ) { // +4 cards + 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 { for (let i=0; i<7; i++) { - player.addCard( this.draw() ); + player.addCard( this._cards.pop() ); } }); 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]; + + async startGame() { + console.log('Game start'); + + this._current_player = 0; + this._turn_count = 0; + + 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 + // } } - updateView() { - const inner_w = window.innerWidth; - const inner_h = window.innerHeight; + async botPlay(player) { + console.log('Turn count: ' + this._turn_count + ', current player: ' + player.name); + await new Promise(resolve => setTimeout(resolve, 1000)); - // Drw top card - this._top_card.drawImageFront(inner_w/2, inner_h/2); + const card = player.playCard(this._top_card); + if (card) { + console.log('player: ' + player.name + ' played card num: ' + card.num + ', color: ' + card.color_n); + this.changeTopCard(card); + } else { + const card = this._cards.pop(); + console.log('player: ' + player.name + ' drawed card num: ' + card.num + ', color: ' + card.color_n); + player.addCard(card); + } - // Draw players card - this._players.forEach( (player) => { - for (let i=0; i { + if (!card.event_is_set) { + card.canvas.addEventListener('click', ()=>{ + this.humanPlay(player, card); + player.removeCard(card); + }); + card.event_is_set = true; } }); } - async startGame() { - await( this.initDeck() ); - await( this.shuffleDeck() ); + async humanPlay(player, card) { + console.log('Turn count: ' + this._turn_count + ', current player: ' + player.name); - let count = 0; - let current_turn = 0; - this._top_card = this.draw(); + this.changeTopCard(card); - console.log('Game start'); + console.log('player: ' + player.name + ' played card num: ' + card.num + ', color: ' + card.color_n); + player.refreshCards(); - while (true) { - console.log('count: ' + count + ', current player: ' + this._players[current_turn].name); - const player = this._players[current_turn]; - const card = await( player.playCard(this._top_card) ); - if (card) { - console.log('player: ' + player.name + ' played card num: ' + card.num + ', color: ' + card.color_n); - card.clear(); - this._used_cards.push(this._top_card); - this._top_card = card; + this._turn_count++; + this.botPlay( this.getNextPlayer() ); + } + + getNextPlayer() { + if (this._reverse) { + if (this._current_player === 0) { + this._current_player = this._players.length-1; } else { - const card = this.draw(); - console.log('player: ' + player.name + ' drawed card num: ' + card.num + ', color: ' + card.color_n); - player.addCard(card); + this._current_player--; } - - this.updateView(); - //await Promise.all([ - // timeout(1000) - //]); - await new Promise(resolve => setTimeout(resolve, 1000)); - - if (player.isEmpty()) { - console.log('player: ' + player.name + ' has no card left. Game end'); - break + } else { + if (this._current_player === this._players.length-1) { + this._current_player = 0; + } else { + this._current_player++; } - - current_turn = (current_turn >= this._players.length-1) ? 0 : ++current_turn; - count++; } + 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(); } } diff --git a/src/styles/uno-game.scss b/src/styles/uno-game.scss index 7cf2740..fa62c6c 100644 --- a/src/styles/uno-game.scss +++ b/src/styles/uno-game.scss @@ -7,14 +7,8 @@ .uno-game-canv-default { position: absolute; 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'); }