沒想到我會突然寫一版HTML5+JS的俄羅斯方塊,只為了練習canvas,雖然最後發現…全部的程式碼三百多行,真正用到canvas才5行左右Orz
/**
* Created by ercrta on 2015/9/10.
* 2015-09-10 13:30~19:00
* 2015-09-11 09:30~12:00
* 2015-09-11 13:00~14:20
* 共約9小時
*/
$(function(){
var GAME_STATUS_INIT=0;
var GAME_STATUS_RUN=1;
var GAME_STATUS_STOP=2;
var GAME_STATUS_END=3;
var MOVE_DIRECTION_DOWN=0;
var MOVE_DIRECTION_LEFT=1;
var MOVE_DIRECTION_RIGHT=2;
var BOX_STATUS_SPACE=0; //空白位置
var BOX_STATUS_MOVE_PUZZLE=1; //移動中的方塊
var BOX_STATUS_FIXED_PUZZLE=2; //固定的方塊
var MAP_WIDTH=8;
var MAP_HEIGHT=19;
var BOX_SIZE=10;
var map;
var speed=1000;
var interval;
var puzzle_position=[];
var puzzle_center=[];
var game_status;
init();
function init(){
set_game_status(GAME_STATUS_INIT);
$("#puzzle_canvas").attr("width",BOX_SIZE*MAP_WIDTH);
$("#puzzle_canvas").attr("height",BOX_SIZE*MAP_HEIGHT);
$("#btn_start").bind('click',function(){
create_empty_map();
do_puzzle_create(create_puzzle());
set_game_status(GAME_STATUS_RUN);
star_game();
});
$("#btn_stop").bind('click',function(){
set_game_status(GAME_STATUS_STOP);
clearInterval(interval);
});
$("#btn_restart").bind('click',function(){
create_empty_map();
do_puzzle_create(create_puzzle());
set_game_status(GAME_STATUS_RUN);
star_game();
});
$("#btn_continue").bind('click',function(){
set_game_status(GAME_STATUS_RUN);
star_game();
});
$(document.body).on('keydown', function(e) {
doKeyDown(e);
});
}
function star_game(){
clearInterval(interval);
interval=setInterval(function(){
var move_success=do_arrow_direction(MOVE_DIRECTION_DOWN);
if(move_success==false){
do_set_fixed_puzzle();
do_clean_fixed_puzzle();
var create_success=do_puzzle_create(create_puzzle());
if(create_success==false){
set_game_status(GAME_STATUS_END);
clearInterval(interval);
alert("End Game");
}
}
},speed);
}
function doKeyDown(e) {
if(game_status!=GAME_STATUS_RUN)return;
var keyID = e.keyCode ? e.keyCode :e.which;
if(keyID === 38 || keyID === 87) { // up arrow and W
do_puzzle_rotation();
}
if(keyID === 39 || keyID === 68) { // right arrow and D
do_arrow_direction(MOVE_DIRECTION_RIGHT);
}
if(keyID === 40 || keyID === 83) { // down arrow and S
do_arrow_direction(MOVE_DIRECTION_DOWN);
}
if(keyID === 37 || keyID === 65) { // left arrow and A
do_arrow_direction(MOVE_DIRECTION_LEFT);
}
}
function set_game_status(_game_status){
game_status=_game_status;
view_buttons(_game_status);
}
function set_map(_map){
map=_map;
view_map();
}
function view_buttons(_game_status){
$("#btn_start").hide();
$("#btn_stop").hide();
$("#btn_restart").hide();
$("#btn_continue").hide();
switch (_game_status){
case GAME_STATUS_INIT:
$("#btn_start").show();
break;
case GAME_STATUS_RUN:
$("#btn_stop").show();
$("#btn_restart").show();
break;
case GAME_STATUS_STOP:
$("#btn_continue").show();
break;
case GAME_STATUS_END:
$("#btn_restart").show();
break;
default :
break;
}
}
function view_map(){
var c=document.getElementById("puzzle_canvas");
var cxt=c.getContext("2d");
for(var i=0;i<MAP_WIDTH;i++){
for(var j=0;j<MAP_HEIGHT;j++){
draw_box(cxt,i,j,map[i][j]);
}
}
}
function draw_box(cxt,x,y,box){
switch (box){
case BOX_STATUS_SPACE:cxt.fillStyle = "#FF0000";break; //red
case BOX_STATUS_MOVE_PUZZLE:cxt.fillStyle = "#00FF00";break; //green
case BOX_STATUS_FIXED_PUZZLE:cxt.fillStyle = "#0000FF";break; //blue
default :
break;
}
cxt.fillRect(x*BOX_SIZE,y*BOX_SIZE,BOX_SIZE,BOX_SIZE);
cxt.strokeRect(x*BOX_SIZE,y*BOX_SIZE,BOX_SIZE,BOX_SIZE);
}
function create_empty_map(){
var tmp_map=new Array(MAP_WIDTH);
for(var i=0;i<MAP_WIDTH;i++){
tmp_map[i]=new Array(MAP_HEIGHT);
for(var j=0;j<MAP_HEIGHT;j++){
tmp_map[i][j]=0;
}
}
set_map(tmp_map);
}
function create_puzzle(){
var random_num=Math.floor((Math.random() * 19) + 1);
switch(random_num){
case 1:return [[0,0],[1,0],[2,0],[1,1]];break; //T
case 2:return [[1,0],[0,1],[1,1],[1,2]];break; //T
case 3:return [[1,0],[0,1],[1,1],[2,1]];break; //T
case 4:return [[0,0],[0,1],[1,1],[0,2]];break; //T
case 5:return [[1,0],[1,1],[1,2],[1,3]];break; //I
case 6:return [[0,0],[1,0],[2,0],[3,0]];break; //I
case 7:return [[0,0],[1,0],[1,1],[2,1]];break; //Z
case 8:return [[1,0],[0,1],[1,1],[0,2]];break; //Z
case 9:return [[1,0],[2,0],[0,1],[1,1]];break; //倒Z
case 10:return [[0,0],[0,1],[1,1],[1,2]];break; //倒Z
case 11:return [[0,0],[0,1],[1,0],[1,1]];break; //田
case 12:return [[0,0],[0,1],[0,2],[1,2]];break; //L
case 13:return [[0,0],[1,0],[2,0],[0,1]];break; //L
case 14:return [[0,0],[1,0],[1,1],[1,2]];break; //L
case 15:return [[2,0],[0,1],[1,1],[2,1]];break; //L
case 16:return [[1,0],[1,1],[0,2],[1,2]];break; //倒L
case 17:return [[0,0],[0,1],[1,1],[2,1]];break; //倒L
case 18:return [[0,0],[1,0],[0,1],[0,2]];break; //倒L
case 19:return [[0,0],[1,0],[2,0],[2,1]];break; //倒L
default :
return [[1,0],[1,1],[1,2],[1,3]];break; //I
}
}
function do_arrow_direction(direction){
switch (direction){
case MOVE_DIRECTION_DOWN:return do_puzzle_move(0,1);break;
case MOVE_DIRECTION_LEFT:return do_puzzle_move(-1,0);break;
case MOVE_DIRECTION_RIGHT:return do_puzzle_move(1,0);break;
default :
break;
}
}
function do_puzzle_create(puzzle){
var new_puzzle_position=[];
var len=puzzle.length;
//計算新位置
for(var i=0;i<len;i++){
var point_x=puzzle[i][0];
var point_y=puzzle[i][1];
new_puzzle_position.push([2+point_x,point_y]);
}
return do_set_puzzle_in_map(new_puzzle_position,[3,1]);
}
function do_puzzle_move(move_x,move_y){
var new_puzzle_position=[];
var len=puzzle_position.length;
//計算新位置
for(var i=0;i<len;i++){
var point_x=puzzle_position[i][0];
var point_y=puzzle_position[i][1];
new_puzzle_position.push([point_x+move_x,point_y+move_y]);
}
return do_set_puzzle_in_map(new_puzzle_position,[puzzle_center[0]+move_x,puzzle_center[1]+move_y]);
}
function do_puzzle_rotation(){
var new_puzzle_position=[];
var len=puzzle_position.length;
//計算新位置
var center_x=puzzle_center[0];
var center_y=puzzle_center[1];
for(var i=0;i<len;i++){
var point_x=puzzle_position[i][0];
var point_y=puzzle_position[i][1];
var new_point_x=-( (point_y-center_y))+center_x;
var new_point_y=-(-(point_x-center_x))+center_y;
new_puzzle_position.push([new_point_x,new_point_y]);
}
return do_set_puzzle_in_map(new_puzzle_position,puzzle_center);
}
function do_set_puzzle_in_map(new_puzzle_position,new_puzzle_center){
var len=new_puzzle_position.length;
//檢查位置
if(chk_puzzle_new_position(new_puzzle_position)==false){
return false;
}
//清除舊位置
do_clean_old_position();
//填上新位置
for(var i=0;i<len;i++){
var point_x=new_puzzle_position[i][0];
var point_y=new_puzzle_position[i][1];
map[point_x][point_y]=BOX_STATUS_MOVE_PUZZLE;
}
//��錄puzzle的位置
puzzle_position=new_puzzle_position;
puzzle_center=new_puzzle_center;
set_map(map);
return true;
}
function chk_puzzle_new_position(new_puzzle_position){
var len=new_puzzle_position.length;
var check_position=true;
for(var i=0;i<len;i++){
var point_x=new_puzzle_position[i][0];
var point_y=new_puzzle_position[i][1];
if(point_x>=MAP_WIDTH || point_x<0){
check_position=false;break;
}
if(point_y>=MAP_HEIGHT || point_y<0){
check_position=false;break;
}
try{
if(map[point_x][point_y]==BOX_STATUS_FIXED_PUZZLE){
check_position=false;break;
}
}catch (e){
alert(point_x+','+point_y);
}
}
return check_position;
}
function do_clean_old_position(){
//清除舊的位置
for(var i=0;i<MAP_WIDTH;i++){
for(var j=0;j<MAP_HEIGHT;j++){
if(map[i][j]==BOX_STATUS_MOVE_PUZZLE){
map[i][j]=BOX_STATUS_SPACE;
}
}
}
}
function do_set_fixed_puzzle(){
for(var i=0;i<MAP_WIDTH;i++){
for(var j=0;j<MAP_HEIGHT;j++){
if(map[i][j]==BOX_STATUS_MOVE_PUZZLE){
map[i][j]=BOX_STATUS_FIXED_PUZZLE;
}
}
}
set_map(map);
}
function do_clean_fixed_puzzle(){
var i,j;
var fixed_row_number=[];
//檢查排滿的行數
for(i=0;i<MAP_HEIGHT;i++){
var flag=true;
for(j=0;j<MAP_WIDTH;j++){
if(map[j][i]!=BOX_STATUS_FIXED_PUZZLE){
flag=false;
break;
}
}
if(flag){
fixed_row_number.push(i);
}
}
//清掉
var len=fixed_row_number.length;
for(i=0;i<len;i++){
var row_num=fixed_row_number[i];
for(j=row_num;j>0;j--){
for(var m=0;m<MAP_WIDTH;m++){
if(j-1>=0) {
map[m][j] = map[m][j - 1];
}else{
map[m][j] = BOX_STATUS_SPACE;
}
}
}
}
set_map(map);
}
});
2015-09-11建立完整程式碼下載v0.0
2015-09-20更新,使用localStorage,視窗關閉後,重新打開檔案,會從上次的狀態續繼遊戲
完整程式碼下載v0.1
這次有碰到兩個稍微困擾我的地方:
1.canvas的大小設定,我是用jquery設的,所以本來是用css的方式設定,結果一設,就變形!!
舊寫法:
//$("#puzzle_canvas").css("width",BOX_SIZE*MAP_WIDTH); //$("#puzzle_canvas").css("height",BOX_SIZE*MAP_HEIGHT);更新成:
$("#puzzle_canvas").attr("width",BOX_SIZE*MAP_WIDTH); $("#puzzle_canvas").attr("height",BOX_SIZE*MAP_HEIGHT);2.旋轉的部份也困擾我了一下,一直鑽牛角尖,想到矩陣相乘去了,事實上只要稍微做一下加減就可以了(因為轉90度比較簡單)
var point_x=puzzle_position[i][0]; var point_y=puzzle_position[i][1]; var new_point_x=-( (point_y-center_y))+center_x; var new_point_y=-(-(point_x-center_x))+center_y;