<head>
<title>标题</title>
</head>
<body>
<style>
span.btn
{
BORDER-RIGHT: #7b9ebd 1px solid
PADDING-RIGHT: 2px
BORDER-TOP: #7b9ebd 1px solid
PADDING-LEFT: 2px
FONT-SIZE: 12px
FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#cecfde)
BORDER-LEFT: #7b9ebd 1px solid
COLOR: black
PADDING-TOP: 2px
BORDER-BOTTOM: #7b9ebd 1px solid
background-color: #CCCCCC
}
</style>
<script language="javascript">
var doing
var candown=0
var wnum=13
var hnum=18
var grid=new Array()
var gridBuf=new Array()
for(i=0i<=hnumi++){
grid[i]=new Array()
gridBuf[i]=new Array()
for(j=0j<=wnumj++){
if(j>0 &&j<wnum &&i<hnum){
grid[i][j]=0
gridBuf[i][j]=0
}else{
grid[i][j]=1
gridBuf[i][j]=1
}
}
}
var boxdata=
[
[
[1,1,1,1],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]
],
[
[1,1,1,0],
[1,0,0,0],
[0,0,0,0],
[0,0,0,0],
],
[
[1,1,1,0],
[0,1,0,0],
[0,0,0,0],
[0,0,0,0]
],
[
[1,1,1,0],
[0,0,1,0],
[0,0,0,0],
[0,0,0,0]
],
[
[1,1,0,0],
[0,1,1,0],
[0,0,0,0],
[0,0,0,0]
],
[
[0,1,1,0],
[1,1,0,0],
[0,0,0,0],
[0,0,0,0]
],
[
[1,1,0,0],
[1,1,0,0],
[0,0,0,0],
[0,0,0,0]
]
]
var colors=["black","#A0A0A0","red","#FF8000","yellow","pink"]
var gotLine=0
var box
var bGameOver=false
function getHeight(arr)
{
var i,j
for(i=3i>=0i--)
for(j=0j<4j++)
if(arr[i][j]) return i
}
function getWidth(arr)
{
var i,j
for(i=3i>=0i--)
for(j=0j<4j++)
if(arr[j][i]) return i
}
function Box(x,y,arr,color)
{
this.arr=arr
this.x=x
this.y=y
this.w=getWidth(arr)
this.h=getHeight(arr)
this.color=color
this.active=true
this.clearOldBox=function()
{
for(var j=0j<=this.hj++)
for(var i=0i<=this.wi++)
if(this.arr[j][i]>0) grid[this.y+j][this.x+i]=0
}
this.putNewBox=function()
{
for(var j=0j<=this.hj++)
for(var i=0i<=this.wi++)
if(this.arr[j][i]>0) grid[this.y+j][this.x+i]=this.color
}
this.moveLeft=function()
{
this.clearOldBox()
var _x=this.x-1
if(this.canMove(_x,this.y)) this.x=_x
this.putNewBox()
drawGrid()
}
this.moveRight=function()
{
this.clearOldBox()
var _x=this.x+1
if(this.canMove(_x,this.y)) this.x=_x
this.putNewBox()
drawGrid()
}
this.moveDown=function()
{
this.clearOldBox()
var _y=this.y+1
if(this.canMove(this.x,_y)){
this.y=_y
this.putNewBox()
drawGrid()
}else{
this.putNewBox()
drawGrid()
checkLineFull()
return
}
}
this.rotate=function()
{
var tmp=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
for(j=0j<=this.hj++)
for(i=0i<=this.wi++)
tmp[this.w-i][j]=this.arr[j][i]
var newBox=new Box(this.x,this.y,tmp,this.color)
this.clearOldBox()
if(! newBox.canMove(this.x,this.y)) this.putNewBox()
else
{
box=newBox
box.putNewBox()
drawGrid()
}
}
this.canMove=function(x,y)
{
for(var j=0j<=this.hj++)
for(var i=0i<=this.wi++)
{
if(grid[y+j][x+i]!=0 &&this.arr[j][i]!=0){ candown=1return false}
}
return true
}
}
function drawGrid()
{
for(var j=0j<hnumj++)
for(var i=0i<wnumi++)
{
if( grid[j][i]!=gridBuf[j][i])
{
paintCell(j,i,grid[j][i])
}
gridBuf[j][i]=grid[j][i]
}
}
function paintCell(i,j,color)
{
var htmlGrid=document.getElementById("TetrisGrid").firstChild
htmlGrid.childNodes[i].childNodes[j].style.backgroundColor=colors[color]
}
function init()
{
var html='<table id="TetrisGrid" cellspacing=1 style="background-color:green"><tbody>'
for(var i=0i<=hnumi++)
{
html+='<tr>'
for(var j=0j<=wnumj++)
{
html+='<td width="20" height="20" style="background-color:'+colors[grid[i][j]]+'"></td>'
}
html+='</tr>\r\n'
}
html+='</tbody></table>'
document.write(html)
}
function checkLineFull()
{
var full,i,j,i2
var y3=box.y+box.h
var y4=box.y
for(i=y3i>=y4)
{
full=1
for(j=0j<wnumj++)
if(grid[i][j]==0){full=0break}
if(full==0){ --icontinue}
for(i2=ii2>0i2--)
for(j=0j<wnumj++)
grid[i2][j]=grid[i2-1][j]
drawGrid()
y4++
gotLine++
}
checkGameOver()
}
function checkGameOver()
{
var bOver=false
for(var j=1j<wnumj++)
if(grid[0][j]>0){ bOver=truebreak}
if(!bOver){
box=new Box((wnum-1)/2,0,boxdata[Math.floor(Math.random()*7)],Math.floor(Math.random()*5)+1)
box.putNewBox()
}
else
{
bGameOver=true
msg.innerHTML="游戏结束! 您的得分为"+gotLine*100
window.clearTimeout(doing)
}
}
function document_onkeydown()
{
if(bGameOver) return
switch(event.keyCode)
{
case 32:
down()
break
case 37:
box.moveLeft()
break
case 39:
box.moveRight()
break
case 38:
box.rotate()
break
case 40:
box.moveDown()
break
case 80:
stop()
break
case 66:
window.location.reload()
break
case 67:
restart()
break
}
}
function down(){
if(window.event.keyCode==32){
clearTimeout(doing)
for(i=0i<hnumi++){
if(candown==0){
box.moveDown()
}else{
break
}
}
candown=0
doing=window.setTimeout('moveDownBox()',interval)
}
}
var interval
function moveDownBox()
{
interval=1000-10*(gotLine>80?80 :gotLine)
msg.innerHTML=" 等级:"+Math.floor(gotLine/10)+";得分:"+gotLine*100
box.moveDown()
doing=window.setTimeout('moveDownBox()',interval)
}
function startGame()
{
init()
doing=window.setTimeout('moveDownBox()',1000)
bGameOver=false
box=new Box((wnum-1)/2,0,boxdata[Math.floor(Math.random()*7)],Math.floor(Math.random()*5)+1)
box.putNewBox()
drawGrid()
}
var status
function stop(){
status=1
window.clearTimeout(doing)
}
function restart(){
if(status==1){
status=0
doing=window.setTimeout('moveDownBox()',1000)
}
}
function keydown(){
if (document.all)document_onkeydown()
}
</script>
<BODY onLoad="window.focus()" onkeydown="keydown()">
<table width="100%" height="100%" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td>
<table id="maintable" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<span class="btn" style="width:100%height:24pxbackground-color:#F0C0C0color:#0000FFvertical-align:middletext-align:center">俄罗斯方块</span></td>
</tr>
<tr>
<td style="height:20pxbackground-color:blackcolor:#00FF00font-size:12px"><table height="20" border="0" cellpadding="0" cellspacing="0">
<tr style="font-size:12pxcolor:#FFFFFF">
<td width="100%"><a style="cursor:hand" onclick="window.location.reload()">开始(B)</a> <a style="cursor:hand" onclick="stop()">暂停(P)</a> <a style="cursor:hand" onclick="restart()">继续(C)</a></td>
</tr>
</table></td>
</tr>
<tr>
<td style="height:20pxbackground-color:blackcolor:#00FF00font-size:12px" id="msg">等级:0;得分:0</td>
</tr>
<tr>
<td height="20">
<script language=javascript>
maintable.style.width=(wnum+1)*20
startGame()
</script>
</td>
</tr>
</table></td>
</tr>
</table>
</body>
</html>
顾名思义,俄罗斯方块自然是俄罗斯人发明的。这位伟人叫做阿列克谢·帕基特诺夫(Alexey Pazhitnov) 。然而,很少有人知道,这个著名的游戏在80年代曾经在法律界掀起轩然大波,那就是著名的俄罗斯方块产权之争。这次产权争夺,几家欢喜,几家哀愁,几家公司倒闭,几家公司赚钱,其中的是是非非,一言难尽。
欧美列强窃取瓜分:版权之争由此开始
1985年6月工作于莫斯科科学计算机中心的阿列克谢·帕基特诺夫在玩过一个拼图游戏之后受到启发,从而制作了一个以Electronica 60(一种计算机)为平台的俄罗斯方块的游戏。后来经瓦丁·格拉西莫夫(Vadim Gerasimov)移植到PC上,并且在莫斯科的电脑界传播。帕基特诺夫因此开始小有名气。
PC版俄罗斯方块在匈牙利的布达佩斯被当地的一群电脑专家移植到了Apple II 和 Commodore 64 上,这些版本的软件引起了当时英国一个叫Andromeda的游戏公司经理罗伯特·斯坦恩(Robert Stein)的注意,他向帕基特诺夫以及匈牙利的电脑专家们收购了俄罗斯方块的版权,并且在买到版权之前把它们倒手卖给了英国的Mirrorsoft (注意不是Microsoft!) 以及美国的Spectrum Holobyte。(什么人……)
1986年11月斯坦恩和帕基特诺夫经过谈判,就版权收购问题未取得成果。斯坦恩甚至直接飞到莫斯科和帕基特诺夫面谈,但是空手而归。由于俄罗斯人对于已经在西方兴起的电子游戏产业知道不多,斯坦恩决定窃取Tetris的版权,于是他放出谣言说这是匈牙利人开发的游戏。
与此同时,PC版的俄罗斯方块已经由英国的Mirrorsoft出品并且在欧洲销售,受到当时人们的极大关注。不仅仅因为这个游戏好玩,而且这是“第一个来自铁幕国家的游戏。”当时的游戏宣传海报上有浓郁的冷战色彩,比如战争画面,加加林太空飞行等。而斯坦恩仍然没有正式合法的版权。
1987年6月斯坦恩最终取得了在IBM-PC及其兼容机上的Tetris的版权,版权机种包括“其他任何电脑系统”。但是,他没有和苏联方面签署协议,也就是说,这个版权是不完全的。(译者注: 这个“其他任何电脑系统”在原文中的描述是"any other computer system",这种说法在当时看来也很不严密,从而为后来的产权之争埋下了伏笔)
1988年1月Tetris在电脑平台的热销,一时造成“洛阳纸贵”(伦敦磁盘贵??)的局面。而当CBS晚报采访了俄罗斯方块之父帕基特诺夫之后,斯坦恩盗窃版权的计划彻底泡汤了。(活该!)一个新的公司ELORG(Electronorgtechinca,苏联一家软件公司)开始和斯坦恩就游戏程序问题进行协商。ELORG的负责人亚历山大·阿列欣科( Alexander Alexinko)知道斯坦恩虽然没有版权,但是会以手中的游戏开发程序为筹码威胁中断谈判。
任天堂也来掺和:三路人马夹击“装在套子里的人”
1988年5月经过几个月的争吵之后,筋疲力尽的斯坦恩终于和ELORG签定了PC俄罗斯方块版权的合约。当时的合约禁止开发街机版和掌机版的方块游戏,而电脑版的Tetris则成为当时最畅销的游戏。
1988年7月斯坦恩与阿列欣科商谈开发街机版俄罗斯方块的问题。阿列欣科当时尚未从斯坦恩那里拿到一分钱的版权费,但是同时的Spectrum 和 Mirrorsoft已经开始向电子游戏商出售了俄罗斯方块的版权。Spectrum 将Tetris的游戏机和PC在日本的版权卖给了Bullet-Proof Software (FC和GB版俄罗斯方块的制作商),而Mirrorsoft则把它在日本和北美的版权卖给了美国的Atari。这样一来两家公司的矛盾就开始了。 1988年11月,BPS在FC上发行的俄罗斯方块(大家不很熟悉的俄罗斯方块1)在日本发售,销量达200万份。
1988年11月随着GB的开发,NOA(任天堂美国分公司)的经理荒川实(任天堂山内溥老爷子的女婿)希望将Tetris做成GB上的游戏。于是他联系了BPS的总裁亨克·罗杰斯(Henk Rogers), 罗杰斯再与斯坦恩联系的时候却吃了闭门羹。于是他直接去莫斯科购买版权。而斯坦恩觉察出风头,也乘飞机前往莫斯科;与此同时,Spectrum的负责人罗伯特·麦克斯韦(Robert Maxwell)的儿子凯文·麦克斯韦(Kevin Maxwell) 也在向莫斯科进发。就这样,三路人马几乎在同时赶到了冰天雪地的红色都市。
1989年2月21日罗杰斯首先会见了ELORG的代表叶甫盖尼·别里科夫(Evgeni Belikov,和那个“装在套子里的人”同名)。他给帕基特诺夫等苏联人留下了深刻印象,并且签了手掌机方块游戏的版权。之后他向俄国人展示了FC版Tetris,这使别里科夫极为震惊。因为他并没有授予罗杰斯家用机的版权!罗杰斯则向他们说这是向TENGEN购买的版权,但是别里科夫也从没听说过TENGEN这个公司的名字。罗杰斯为了缓和尴尬的局面,将斯坦恩隐瞒的事实如数告诉了别里科夫,并且答应付给苏联方面更多支票作为已经卖出的FC版俄罗斯方块的版权费用。这时罗杰斯发现自己有机会买到Tetris全部机种的版权(但是当时还没买),虽然Atari会对他虎视耽耽,但是别忘了,他和BPS的背后还有任天堂这个大靠山给自己撑腰。
注意:罗伯特·斯坦恩原先所签的协议只是电脑版Tetris的版权,其他的版权并不是他的。
后来,斯坦恩和ELORG重新签署了协议。别里科夫强迫他重签的合约中修改的内容是:“电脑的定义:包含有中央处理器,监视器,磁盘驱动器,键盘和操作系统的机器”。而斯坦恩当时却没有仔细看这些定义。(这回轮到他犯混了……)后来他才意识到这是罗杰斯从自己手中抢走版权而耍的花招。但是为时已晚。第二天他被告知虽然签署的文件已经不能改后来,但是他还可以得到街机版Tetris的开发权。三天之后,他签下了街机版的协议。
FC版权风云再起:
充分体现国家干预经济1989年2月22日凯文·麦克斯韦访问了ELORG。别里科夫拿出罗杰斯给他的FC游戏卡向他询问这件事情。麦克斯韦在卡带上看到了Mirrorsoft的名字后才想起他的公司已经把部分版权倒卖给了Atari。(糊涂人办糊涂事……)当他想继续谈街急和手掌机版权的问题的时候,却发现自己他能够签的,就只有除电脑,街机,家用机和掌机以外的协议了。(其实等于没有协议可签,除非他发明一种新的娱乐系统,比方说俄罗斯方块积木……)在糊涂之余这家伙灵机一动,告诉别里科夫说此卡带为盗版(汗……),然后也要签家用机的协议。最后的结果是:凯文·麦克斯韦只带走一张白纸,罗伯特·斯坦恩带走了街机协议书。由于麦克斯韦声称所有的FC卡都是盗版,ELORG保留了家用机的版权,没卖给任何人。假如麦克斯韦想获得家用机版权的话,就必须出价比任天堂高才行。亨克·罗杰斯买到了掌机的版权,并且通知了荒川实。BPS就制作GB版Tetris向任天堂达成交易:这笔交易额高大500万-1000万美元。
GB版的俄罗斯方块
GB版俄罗斯方块1989年3月15日亨克·罗杰斯回到莫斯科,并且代表任天堂出巨资收购家用机版Tetris的版权。版权费的价格虽然没有向外界透露,但是这个数字将是Mirrorsoft永远拿不出来的。连荒川实和NOA的首席执行官霍华德·林肯(Howard Lincoln)都亲自前往苏联助阵。
1989年3月22日ELORG和任天堂的家用机协议终于达成。任天堂方面坚持加入一款声明,在协议签定之后,如果和其他出现法律纠纷,苏联方面必须派人去美国的法庭上做证。实际上,这种法律上的争端将是不可避免的。据说ELORG仅仅得到的定金有300-500万美元之多。别里科夫向Mirrorsoft通知,说Mirrorsoft, Andromeda和Tengen都没有家用机的版权,现在版权都归任天堂所有。当天晚上任天堂和BPS的头目们在莫斯科酒店里举行了庆祝party。
(各位看明白了,现在家用机和掌机的版权已经被任天堂和BPS分别掌握在手中。无论是Atari还是Tengen都没有权利制作FC版的俄罗斯方块。)
1989年3月31日霍华德·林肯 愉快(幸灾乐祸?)地向Atari发去最后通牒(传真),告诉他们立刻停止FC(NES)版的俄罗斯方块游戏。这使得Atari和麦克斯韦都十分震怒。他们以Tengen的名义回信说在4月7日那天他们就已经享有家用机俄罗斯方块的版权了。
1989年4月13日Tengen撰写了一份申请书,要求拥有Tetris的“影音作品,源程序和游戏音乐”版权。但是申请书中并没有提及阿列克谢·帕基特诺夫和任天堂的游戏版权问题。(忽视了阿列克谢·帕基特诺夫真是个大错误!)
与此同时,麦克斯韦利用自己掌握的媒体势力,企图夺回Tetris的阵地。甚至搬出了苏联与英国政府,对俄罗斯方块版权问题进行干预。(好大的面子啊!)结果挑起了苏共(!)与ELORG之间的矛盾。甚至连戈尔巴乔夫都向麦克斯韦保证“以后不用担心日本公司的问题”。(汗……这位麦克斯韦果然不是善主……伟大的苏联政府都对俄罗斯方块关注起来了……)在4月晚些时候,霍华德·林肯回到莫斯科的时候,发现ELORG已经在苏联政府的打压下抬不起头来,而就在那天半夜,NOA方面给他打电话,说Tengen已经起诉了任天堂。(山雨欲来风满楼啊……)
第二天,他面会了别里科夫,帕基特诺夫和其他几位ELORG的成员,以确保他们能够为任天堂的官司佐证。(这回合同里的条款可生效了)随后NOA立刻反诉Tengen,并且开始收集证据。
FC是电脑还是家用机?任天堂大获全胜
1989年5月17日Tengen在USA Today上登载了大幅Tertis广告,虽然法庭大战已经迫在眉睫。
1989年6月Tengen与任天堂的案子终于开庭审理论战主要围绕一个议题展开:NES(FC)究竟是电脑,还是电子游戏机。(大家不许笑,在法庭上这可是很严肃的话题)Atari认为NES是电脑系统,因为它拥有扩展机能,而且日本的Famicom也有网络功能存在。而任天堂的证据则更加切题:ELORG中的苏联人从来没有意向出售Tetris的家用机版权,而所谓的“电脑”的概念则早在和斯坦恩的协议中提到了。
1989年6月15日法庭召开听证会,讨论关于任天堂和Tengen互相命令对方终止生产和销售各自的Tetris软件的行为。法官福恩·史密斯(Fern Smith)宣布Mirrorsoft 与 Spectrum Holobyte均没有家用机版权,因此他们提供给Tengen的权利也不能生效。任天堂的请求最后得到了许可。
1989年6月21日Tengen版的俄罗斯方块全部撤下了货架,该游戏卡带的生产也被迫中止。数十万份软件留在包装盒里,封存在仓库中。
1989年7月任天堂NES版Tetris在美国发售。全美销量大约300万。与此同时,和GB版Tetris捆绑销售的Game Boy席卷美国,美利坚大地上刮起一阵方块旋风。
关于Tetris的混战此时已经告一段落。而任天堂和Tengen之间的法庭纠纷则一直持续到1993年。
尾声:狂赚钱与做白工?!还好玩家没吃亏
尾声Atari Games仍然开发了街机版的Tetris,共卖出约2万台机器。近来Atari Games 被 Williams/WMS收购,而那些封存在仓库里的NES版Tetris的命运则没人知道。Tengen不能从其他途径把它们处理掉,所以估计这些软件都被销毁了。但是据说仍然有约10万份Tengen版的Tetris流入了市场。
我们今天在64合一等游戏D版卡里玩到的所谓“俄罗斯方块2”其实就是当年的Tengen版,平心而论,这一版的方块比BPS的版本要好玩许多。首先,这版的操作感和按键设定十分到位,AB分别是正转和反转,而BPS版是用十字键的下来转动,只支持一个方向,按A就直接“啪”地落下来,手感十分不爽;其次,它支持的二人对战,与电脑竞争和合作的模式也让人耳目一新。还有,音乐也是没的说。
罗伯特·斯坦恩,这个版权问题的始作俑者,在Tetris上总共只赚了25万美元。本来他可以挣多点钱的,但是Atari和Mirrorsoft在付他版税的时候没有给足。(应得的报应……)Spectrum Holobyte 则需要和ELORG重新协商,以确保电脑版Tetris的版权。
罗伯特·麦克斯韦的媒体堡垒在混战中逐渐分崩离析,老麦克斯韦在做生意时做幕后黑手的事实也在调查中,而他却突然暴病身亡。(气死的……)Mirrorsoft 英国公司也惨淡地退出了历史舞台。
真正的大赢家是BPS的总裁亨克·罗杰斯,还有幕后的任天堂。俄罗斯方块究竟为任天堂赚了多少银子呢?答案恐怕永远说不清了。想一想吧,在美国GB都是和Tetris捆绑销售,以增加GB的出货量……然后因为Tetris买了GB的人还会买其他的GB卡……要是这么算起来的话,那利润简直就像滚雪球一样了。现在GB版的Tetris(Z版)总共生产了3000万张。(后来GB的俄罗斯方块又在SFC上出了复刻版,和《马里奥医生》一起出现在屏幕上,成为不朽之作。)
至于苏联方面,除了苏联政府,谁也没有从Tetris那里得到多少好处。苏联解体之后,原ELORG的人员都四散到了全国乃至世界各地,许多人继续开发游戏(比方说帕基特诺夫)。
阿列克谢·帕基特诺夫几乎没有从Tetris上赚到一分钱。ELORG本来打算给他Tetris的销售权,但是旋即取消了这笔交易。不过帕基特诺夫仍然为自己能够制作出这么一个世界闻名的优秀游戏而欣慰。他从科学院里得到一台286(当时在苏联可是了不起的电脑)作为奖励。而且分到了比同事们家宽敞明亮的房子。在1996年,亨克·罗杰斯支付给他一笔报酬(还算是个知恩图报的人),帕基特诺夫组建了Tetris Company LLC 公司,终于能够自己创作游戏,并且收取版权费了。
注:当年俄罗斯方块红遍世界的各个角落,一个本来是吃大锅饭的人在消极怠工的时候发明的娱乐工具成了造福全人类的宝贝,它的价值远远超越了开发这个软件时候的预想。Atari虽然在法庭上惨败,但是拜亚洲盗版商人所赐,Tengen版的俄罗斯方块已经在中国玩家心目中生根发芽,长叶开花,任天堂的正统Tetris在中国反而没人玩了。其实说了那么多,归根到底,平平淡淡才是真。有机会的话,下载一个FC的模拟器和一个64合一的经典ROM,回家体会一下俄罗斯方块的魅力吧……
手机游戏---俄罗斯方块http://www.onlinedown.net/soft/40673.htmhttp://games.sina.com.cn/m/c/2004-10-21/1069.shtml
http://www.gamezero.cn/game8878.html
俄罗斯方块——java源代码提供
import java.awt.*
import java.awt.event.*
//俄罗斯方块类
public class ERS_Block extends Frame{
public static boolean isPlay=false
public static int level=1,score=0
public static TextField scoreField,levelField
public static MyTimer timer
GameCanvas gameScr
public static void main(String[] argus){
ERS_Block ers = new ERS_Block("俄罗斯方块游戏 V1.0 Author:Vincent")
WindowListener win_listener = new WinListener()
ers.addWindowListener(win_listener)
}
//俄罗斯方块类的构造方法
ERS_Block(String title){
super(title)
setSize(600,480)
setLayout(new GridLayout(1,2))
gameScr = new GameCanvas()
gameScr.addKeyListener(gameScr)
timer = new MyTimer(gameScr)
timer.setDaemon(true)
timer.start()
timer.suspend()
add(gameScr)
Panel rightScr = new Panel()
rightScr.setLayout(new GridLayout(2,1,0,30))
rightScr.setSize(120,500)
add(rightScr)
//右边信息窗体的布局
MyPanel infoScr = new MyPanel()
infoScr.setLayout(new GridLayout(4,1,0,5))
infoScr.setSize(120,300)
rightScr.add(infoScr)
//定义标签和初始值
Label scorep = new Label("分数:",Label.LEFT)
Label levelp = new Label("级数:",Label.LEFT)
scoreField = new TextField(8)
levelField = new TextField(8)
scoreField.setEditable(false)
levelField.setEditable(false)
infoScr.add(scorep)
infoScr.add(scoreField)
infoScr.add(levelp)
infoScr.add(levelField)
scorep.setSize(new Dimension(20,60))
scoreField.setSize(new Dimension(20,60))
levelp.setSize(new Dimension(20,60))
levelField.setSize(new Dimension(20,60))
scoreField.setText("0")
levelField.setText("1")
//右边控制按钮窗体的布局
MyPanel controlScr = new MyPanel()
controlScr.setLayout(new GridLayout(5,1,0,5))
rightScr.add(controlScr)
//定义按钮play
Button play_b = new Button("开始游戏")
play_b.setSize(new Dimension(50,200))
play_b.addActionListener(new Command(Command.button_play,gameScr))
//定义按钮Level UP
Button level_up_b = new Button("提高级数")
level_up_b.setSize(new Dimension(50,200))
level_up_b.addActionListener(new Command(Command.button_levelup,gameScr))
//定义按钮Level Down
Button level_down_b =new Button("降低级数")
level_down_b.setSize(new Dimension(50,200))
level_down_b.addActionListener(new Command(Command.button_leveldown,gameScr))
//定义按钮Level Pause
Button pause_b =new Button("游戏暂停")
pause_b.setSize(new Dimension(50,200))
pause_b.addActionListener(new Command(Command.button_pause,gameScr))
//定义按钮Quit
Button quit_b = new Button("退出游戏")
quit_b.setSize(new Dimension(50,200))
quit_b.addActionListener(new Command(Command.button_quit,gameScr))
controlScr.add(play_b)
controlScr.add(level_up_b)
controlScr.add(level_down_b)
controlScr.add(pause_b)
controlScr.add(quit_b)
setVisible(true)
gameScr.requestFocus()
}
}
//重写MyPanel类,使Panel的四周留空间
class MyPanel extends Panel{
public Insets getInsets(){
return new Insets(30,50,30,50)
}
}
//游戏画布类
class GameCanvas extends Canvas implements KeyListener{
final int unitSize = 30//小方块边长
int rowNum//正方格的行数
int columnNum//正方格的列数
int maxAllowRowNum//允许有多少行未削
int blockInitRow//新出现块的起始行坐标
int blockInitCol//新出现块的起始列坐标
int [][] scrArr//屏幕数组
Block b//对方快的引用
//画布类的构造方法
GameCanvas(){
rowNum = 15
columnNum = 10
maxAllowRowNum = rowNum - 2
b = new Block(this)
blockInitRow = rowNum - 1
blockInitCol = columnNum/2 - 2
scrArr = new int [32][32]
}
//初始化屏幕,并将屏幕数组清零的方法
void initScr(){
for(int i=0i<rowNumi++)
for (int j=0j<columnNumj++)
scrArr[j]=0
b.reset()
repaint()
}
//重新刷新画布方法
public void paint(Graphics g){
for(int i = 0i <rowNumi++)
for(int j = 0j <columnNumj++)
drawUnit(i,j,scrArr[j])
}
//画方块的方法
public void drawUnit(int row,int col,int type){
scrArr[row][col] = type
Graphics g = getGraphics()
tch(type){ //表示画方快的方法
case 0: g.setColor(Color.black)break//以背景为颜色画
case 1: g.setColor(Color.blue)break//画正在下落的方块
case 2: g.setColor(Color.magenta)break//画已经落下的方法
}
g.fill3DRect(col*unitSize,getSize().height-(row+1)*unitSize,unitSize,unitSize,true)
g.dispose()
}
public Block getBlock(){
return b//返回block实例的引用
}
//返回屏幕数组中(row,col)位置的属性值
public int getScrArrXY(int row,int col){
if (row <0 || row >= rowNum || col <0 || col >= columnNum)
return(-1)
else
return(scrArr[row][col])
}
//返回新块的初始行坐标方法
public int getInitRow(){
return(blockInitRow)//返回新块的初始行坐标
}
//返回新块的初始列坐标方法
public int getInitCol(){
return(blockInitCol)//返回新块的初始列坐标
}
//满行删除方法
void deleteFullLine(){
int full_line_num = 0
int k = 0
for (int i=0i<rowNumi++){
boolean isfull = true
L1:for(int j=0j<columnNumj++)
if(scrArr[j] == 0){
k++
isfull = false
break L1
}
if(isfull) full_line_num++
if(k!=0 &&k-1!=i &&!isfull)
for(int j = 0j <columnNumj++){
if (scrArr[j] == 0)
drawUnit(k-1,j,0)
else
drawUnit(k-1,j,2)
scrArr[k-1][j] = scrArr[j]
}
}
for(int i = k-1 i <rowNumi++){
for(int j = 0j <columnNumj++){
drawUnit(i,j,0)
scrArr[j]=0
}
}
ERS_Block.score += full_line_num
ERS_Block.scoreField.setText(""+ERS_Block.score)
}
//判断游戏是否结束方法
boolean isGameEnd(){
for (int col = 0 col <columnNumcol ++){
if(scrArr[maxAllowRowNum][col] !=0)
return true
}
return false
}
public void keyTyped(KeyEvent e){
}
public void keyReleased(KeyEvent e){
}
//处理键盘输入的方法
public void keyPressed(KeyEvent e){
if(!ERS_Block.isPlay)
return
tch(e.getKeyCode()){
case KeyEvent.VK_DOWN:b.fallDown()break
case KeyEvent.VK_LEFT:b.leftMove()break
case KeyEvent.VK_RIGHT:b.rightMove()break
case KeyEvent.VK_SPACE:b.leftTurn()break
}
}
}
//处理控制类
class Command implements ActionListener{
static final int button_play = 1//给按钮分配编号
static final int button_levelup = 2
static final int button_leveldown = 3
static final int button_quit = 4
static final int button_pause = 5
static boolean pause_resume = true
int curButton//当前按钮
GameCanvas scr
//控制按钮类的构造方法
Command(int button,GameCanvas scr){
curButton = button
this.scr=scr
}
//按钮执行方法
public void actionPerformed (ActionEvent e){
tch(curButton){
case button_play:if(!ERS_Block.isPlay){
scr.initScr()
ERS_Block.isPlay = true
ERS_Block.score = 0
ERS_Block.scoreField.setText("0")
ERS_Block.timer.resume()
}
scr.requestFocus()
break
case button_levelup:if(ERS_Block.level <10){
ERS_Block.level++
ERS_Block.levelField.setText(""+ERS_Block.level)
ERS_Block.score = 0
ERS_Block.scoreField.setText(""+ERS_Block.score)
}
scr.requestFocus()
break
case button_leveldown:if(ERS_Block.level >1){
ERS_Block.level--
ERS_Block.levelField.setText(""+ERS_Block.level)
ERS_Block.score = 0
ERS_Block.scoreField.setText(""+ERS_Block.score)
}
scr.requestFocus()
break
case button_pause:if(pause_resume){
ERS_Block.timer.suspend()
pause_resume = false
}else{
ERS_Block.timer.resume()
pause_resume = true
}
scr.requestFocus()
break
case button_quit:System.exit(0)
}
}
}
//方块类
class Block {
static int[][] pattern = {
{0x0f00,0x4444,0x0f00,0x4444},//用十六进至表示,本行表示长条四种状态
{0x04e0,0x0464,0x00e4,0x04c4},
{0x4620,0x6c00,0x4620,0x6c00},
{0x2640,0xc600,0x2640,0xc600},
{0x6220,0x1700,0x2230,0x0740},
{0x6440,0x0e20,0x44c0,0x8e00},
{0x0660,0x0660,0x0660,0x0660}
}
int blockType//块的模式号(0-6)
int turnState//块的翻转状态(0-3)
int blockState//快的下落状态
int row,col//块在画布上的坐标
GameCanvas scr
//块类的构造方法
Block(GameCanvas scr){
this.scr = scr
blockType = (int)(Math.random() * 1000)%7
turnState = (int)(Math.random() * 1000)%4
blockState = 1
row = scr.getInitRow()
col = scr.getInitCol()
}
//重新初始化块,并显示新块
public void reset(){
blockType = (int)(Math.random() * 1000)%7
turnState = (int)(Math.random() * 1000)%4
blockState = 1
row = scr.getInitRow()
col = scr.getInitCol()
dispBlock(1)
}
//实现“块”翻转的方法
public void leftTurn(){
if(assertValid(blockType,(turnState + 1)%4,row,col)){
dispBlock(0)
turnState = (turnState + 1)%4
dispBlock(1)
}
}
//实现“块”的左移的方法
public void leftMove(){
if(assertValid(blockType,turnState,row,col-1)){
dispBlock(0)
col--
dispBlock(1)
}
}
//实现块的右移
public void rightMove(){
if(assertValid(blockType,turnState,row,col+1)){
dispBlock(0)
col++
dispBlock(1)
}
}
//实现块落下的操作的方法
public boolean fallDown(){
if(blockState == 2)
return(false)
if(assertValid(blockType,turnState,row-1,col)){
dispBlock(0)
row--
dispBlock(1)
return(true)
}else{
blockState = 2
dispBlock(2)
return(false)
}
}
//判断是否正确的方法
boolean assertValid(int t,int s,int row,int col){
int k = 0x8000
for(int i = 0i <4i++){
for(int j = 0j <4j++){
if((int)(pattern[t][s]&k) != 0){
int temp = scr.getScrArrXY(row-i,col+j)
if (temp<0||temp==2)
return false
}
k = k >>1
}
}
return true
}
//同步显示的方法
public synchronized void dispBlock(int s){
int k = 0x8000
for (int i = 0i <4i++){
for(int j = 0j <4j++){
if(((int)pattern[blockType][turnState]&k) != 0){
scr.drawUnit(row-i,col+j,s)
}
k=k>>1
}
}
}
}
//定时线程
class MyTimer extends Thread{
GameCanvas scr
public MyTimer(GameCanvas scr){
this.scr = scr
}
public void run(){
while(true){
try{
sleep((10-ERS_Block.level + 1)*100)
}
catch(InterruptedException e){}
if(!scr.getBlock().fallDown()){
scr.deleteFullLine()
if(scr.isGameEnd()){
ERS_Block.isPlay = false
suspend()
}else
scr.getBlock().reset()
}
}
}
class WinListener extends WindowAdapter{
public void windowClosing (WindowEvent l){
System.exit(0)
}
}