ActionScript3.0の1行テトリスを読む
AS3一行でテトリス http://d.hatena.ne.jp/flashrod/20070120#1169261232 のソースコードを読んでみようと思う。
まずはソースにインデントを付けてみる。
package{
import flash.display.*;
import flash.text.*;
public class Tetris extends Sprite{
var W=10,
H=20,
T=16,
C=[0x000000,0x00FFFF,0xFFFF00,0x22FF22,0xFF2222,0x4444FF,0xFF8844,0xFF22FF],
P=[[[1,1,1,1]],[[0,2,0],[2,2,2]],[[3,3,0],[0,3,3]],[[0,4,4],[4,4,0]],[[5,5],[5,0],[5,0]],[[6,6],[0,6],[0,6]],[[7,7],[7,7]]],
s=[30,20,10,5],
b=[],
p,
r,
t=new TextField(),
f=[],
l=0,
c=0,
g,
j:int,
i:int,
v:int,
u:int;
public function Tetris(){
t.x=W*T;
t.autoSize="left";
t.text="Next:";
addChild(t);
for(j=0;j<H;++j){
b[j]=[];
for(i=0;i<W;++i)
b[j][i]=0
}
f[72]=function(){v-=w(v-1,u,p)};
f[74]=function(){m(1)};
f[75]=function(){m(0)};
f[76]=function(){v+=w(v+1,u,p)};
f[32]=function(){d();h()};
stage.addEventListener("keyDown",
function(e){
if(f[e.keyCode]){f[e.keyCode]();n()}
}
);
h();
h();
addEventListener("enterFrame",
function(e){
if(--c<0){
c=s[int(l/10)];
if(w(v,u+1,p)){
++u;
n()
}else{
d();
h()
}
}
}
)
}
function m(o){
var q=new Array(p[0].length);
for(j=0;j<q.length;++j)
q[j]=[];
for(j=0;j<p.length;++j)
for(i=0;i<q.length;++i)
if(o)
q[i][p.length-1-j]=p[j][i];
else
q[q.length-1-i][j]=p[j][i];
if(w(v,u,q))
p=q
}
function n(){
with(graphics){
clear();
for(j=0;j<H;++j)
for(i=0;i<W;++i){
g=0;
if(u<=j&&j<(u+p.length)&&v<=i&&i<(v+p[0].length))
g=p[j-u][i-v];
if(!g)
g=b[j][i];
beginFill(C[g]);
drawRect(i*T,j*T,T,T)
}
for(j=0;j<r.length;++j)
for(i=0;i<r[j].length;++i){
beginFill(C[r[j][i]]);
drawRect((i+W+1)*T,(j+2)*T,T,T)
}
}
}
function w(x:int,y:int,p){
for(j=0;j<p.length;++j){
if(0>(y+j)||(y+j)>=H)
return 0;
for(i=0;i<p[j].length;++i){
if(0>(x+i)||(x+i)>=W)
return 0;
if(p[j][i]&&b[y+j][x+i])
return 0
}
}
return 1
}
function d(){
for(;w(v,u+1,p);u++)
;
for(j=0;j<p.length;++j)
for(i=0;i<p[j].length;++i)
if(p[j][i])
b[u+j][v+i]=p[j][i];
for(j=0;j<H;++j)
if(b[j].indexOf(0)<0){
b.splice(j,1);
b.unshift([]);
for(i=0;i<W;++i)
b[0][i]=0
}
l++;
if(l/10>=s.length)
l=0
}
function h(){
p=r;
if(p){
v=(W-p[0].length)/2;
u=0;
if(!w(v,u,p))
t.text="GAME OVER"
}
r=P[int(Math.random()*P.length)]
}
}
}変数名、関数名が短縮されているので読みにくい。 動作は問題ない。
この前のC言語テトリス http://itouhiro.hatenablog.com/entry/20121119/tetris と比べて以下の違いがある。
- 「次の落下ブロック」を表示
- 落下スピードがプレイ中にアップ
- 回転方向が2通り
- 「一気に落下」ボタンがある
- 「落下スピードアップ」ボタンがない
実際のFlash。一度Flashをクリックすると操作可能。 遊び方は、[H][L]で左と右、[J][K]で回転、[SPACE]で落とす
https://sites.google.com/site/itouhiro/2012/20121120-Tetris.swf
とりあえずコンパイル時に警告が出ないよう、型情報を追加したりimportを追加。
--- Tetris2.as 2012-11-20 12:30:22.426523700 +0900
+++ Tetris.as 2012-11-20 12:39:48.811919100 +0900
@@ -1,26 +1,29 @@
package{
import flash.display.*;
import flash.text.*;
+ import flash.events.Event;
+ import flash.events.KeyboardEvent;
public class Tetris extends Sprite{
- var W=10,
- H=20,
- T=16,
- C=[0x000000,0x00FFFF,0xFFFF00,0x22FF22,0xFF2222,0x4444FF,0xFF8844,0xFF22FF],
- P=[[[1,1,1,1]],[[0,2,0],[2,2,2]],[[3,3,0],[0,3,3]],[[0,4,4],[4,4,0]],[[5,5],[5,0],[5,0]],[[6,6],[0,6],[0,6]],[[7,7],[7,7]]],
- s=[30,20,10,5],
- b=[],
- p,
- r,
- t=new TextField(),
- f=[],
- l=0,
- c=0,
- g,
+ private var
+ W:int=10,
+ H:int=20,
+ T:int=16,
+ C:Array=[0x000000,0x00FFFF,0xFFFF00,0x22FF22,0xFF2222,0x4444FF,0xFF8844,0xFF22FF],
+ P:Array=[[[1,1,1,1]],[[0,2,0],[2,2,2]],[[3,3,0],[0,3,3]],[[0,4,4],[4,4,0]],[[5,5],[5,0],[5,0]],[[6,6],[0,6],[0,6]],[[7,7],[7,7]]],
+ s:Array=[30,20,10,5],
+ b:Array=[],
+ p:Array,
+ r:Array,
+ t:TextField=new TextField(),
+ f:Array=[],
+ l:int=0,
+ c:int=0,
+ g:int,
j:int,
i:int,
v:int,
u:int;
- public function Tetris(){
+ public function Tetris():void{
t.x=W*T;
t.autoSize="left";
t.text="Next:";
@@ -30,20 +33,20 @@
for(i=0;i<W;++i)
b[j][i]=0
}
- f[72]=function(){v-=w(v-1,u,p)};
- f[74]=function(){m(1)};
- f[75]=function(){m(0)};
- f[76]=function(){v+=w(v+1,u,p)};
- f[32]=function(){d();h()};
+ f[72]=function():void{v-=w(v-1,u,p)};
+ f[74]=function():void{m(1)};
+ f[75]=function():void{m(0)};
+ f[76]=function():void{v+=w(v+1,u,p)};
+ f[32]=function():void{d();h()};
stage.addEventListener("keyDown",
- function(e){
+ function(e:KeyboardEvent):void{
if(f[e.keyCode]){f[e.keyCode]();n()}
}
);
h();
h();
addEventListener("enterFrame",
- function(e){
+ function(e:Event):void{
if(--c<0){
c=s[int(l/10)];
if(w(v,u+1,p)){
@@ -57,8 +60,8 @@
}
)
}
- function m(o){
- var q=new Array(p[0].length);
+ private function m(o:int):void{
+ var q:Array=new Array(p[0].length);
for(j=0;j<q.length;++j)
q[j]=[];
for(j=0;j<p.length;++j)
@@ -70,7 +73,7 @@
if(w(v,u,q))
p=q
}
- function n(){
+ private function n():void{
with(graphics){
clear();
for(j=0;j<H;++j)
@@ -90,7 +93,7 @@
}
}
}
- function w(x:int,y:int,p){
+ private function w(x:int,y:int,p:Array):int{
for(j=0;j<p.length;++j){
if(0>(y+j)||(y+j)>=H)
return 0;
@@ -103,7 +106,7 @@
}
return 1
}
- function d(){
+ private function d():void{
for(;w(v,u+1,p);u++)
;
for(j=0;j<p.length;++j)
@@ -121,7 +124,7 @@
if(l/10>=s.length)
l=0
}
- function h(){
+ private function h():void{
p=r;
if(p){
v=(W-p[0].length)/2;これを読んでみる。
まずは、最初に実行されるconstructor(function Tetris())から読んでいく。
t.x=W*T;
t.autoSize="left";
t.text="Next:";
addChild(t);「Next:」という文字列を表示。Wはテトリス競技画面の横幅。このテトリスも横10x縦20なんですね。Tは落下ブロック(テトリミノと呼ぶらしい)を構成する個々の正方形の縦・横幅。
for(j=0;j<H;++j){
b[j]=[];
for(i=0;i<W;++i)
b[j][i]=0
}10x20の競技フィールドを 0 で埋めましたよ。Hはテトリス競技画面の縦の長さ。変数bはboardの略だろうか。
f[72]=function():void{v-=w(v-1,u,p)};
f[74]=function():void{m(1)};
f[75]=function():void{m(0)};
f[76]=function():void{v+=w(v+1,u,p)};
f[32]=function():void{d();h()};
stage.addEventListener("keyDown",
function(e:KeyboardEvent):void{
if(f[e.keyCode]){f[e.keyCode]();n()}
}
);キー操作を定義。 72 h 74 j 75 k 76 l 32 Space のキーコード。 関数w()は落下ブロックを横に移動させる。 関数m()は落下ブロックを回転させる。 関数d()はライン消し判定のようだ。 関数h()はゲームオーバー判定のようだ。 関数n()は画面表示のようだ。
h();
h();このh()で、画面表示か、データ初期化か、をしている。まずはここから追うのがいいだろうけど、constructorを続けて見る。
addEventListener("enterFrame",
function(e:Event):void{
if(--c<0){
c=s[int(l/10)];
if(w(v,u+1,p)){
++u;
n()
}else{
d();
h()
}
}
}
)ゲームで1fps進むごとに(つまり15fpsなら1秒に15回)実行する。c(おそらくcounterの略)が0以下になると、cを再設定して(ゲーム中に落下速度が速くなるのはこのcを減らしているのだろう)、次のw関数に関してはあとで調べる。
これでconstructorは終わり。 変数が何を示しているかよくわからない。
変数Cは明らかに色データだろう。
C:Array=[0x000000,0x00FFFF,0xFFFF00,0x22FF22,0xFF2222,0x4444FF,0xFF8844,0xFF22FF],関数nの中を見ると、
beginFill(C[g]);となっている→gはgroupの略で、落下タイプの種別を表すようだ。色からgroupを推測する。 group 色
0 黒
1 水色 |
2 黄色 凸
3 黄緑 ┌┘
4 赤 _|~
5 青 ┌
6 オレンジ ┐
7 ピンク ■
となっているようだ。
変数Pはブロック形状だ。
P:Array=[
[[1,1,1,1]],
[[0,2,0],[2,2,2]],
[[3,3,0],[0,3,3]],
[[0,4,4],[4,4,0]],
[[5,5],[5,0],[5,0]],
[[6,6],[0,6],[0,6]],
[[7,7],[7,7]]
],この
[[1,1,1,1]],は以下の画像のことだ。
[[0,2,0],[2,2,2]],は以下の画像のことだ。2という数字は色をあらわしている。
ということは前回のC言語テトリスと違い、(0,0)が棒の真ん中ではなく端になっている。回転を(0,0)ですると端が回転軸になるのだが‥‥Flashで実演すると確かに端が回転の軸になっていた。C言語テトリスのほうが質が高くてプレイしやすいという印象を受けていたのは、これのせいか。
この変数Pがどこで使用されているか追いかけてみると、関数hの中でrに代入されている。配列変数rは関数n()の中で、
for(j=0;j<r.length;++j)
for(i=0;i<r[j].length;++i){
beginFill(C[r[j][i]]);
drawRect((i+W+1)*T,(j+2)*T,T,T)
}で使われているだけ。 関数h()で
p=r;とあるので、pも落下ブロック形状である。 関数m()の変数qも、pからコピーされるブロック形状。
constructorの中で最初に呼ばれる関数、h()を見てみる。
private function h():void{
p=r;
if(p){
v=(W-p[0].length)/2;
u=0;
if(!w(v,u,p))
t.text="GAME OVER"
}
r=P[int(Math.random()*P.length)]
}(一番最初にh()が呼ばれたときは) 変数r:Arrayは初期値が設定されてないので、nullが入る(参考: http://f-site.org/articles/2010/07/31041620.html ) つまり変数pもnullなので、if(p)は実行されない。 r=P[..] で、変数rに [[0,2,0],[2,2,2]] などの内容が入る。
(2回目にh()が呼ばれたときは) グローバル変数rに[[0,2,0],[2,2,2]]とか入っている。→if(p)の中が実行される。 v=(10-3)/2 = 3 u=0 w(3,0,[[0,2,0],[2,2,2]])が呼ばれる。w()はゲームオーバー判定でもあるらしい。
関数w()
private function w(x:int,y:int,p:Array):int{
for(j=0;j<p.length;++j){
if(0>(y+j)||(y+j)>=H)
return 0;
for(i=0;i<p[j].length;++i){
if(0>(x+i)||(x+i)>=W)
return 0;
if(p[j][i]&&b[y+j][x+i])
return 0
}
}
return 1
}



0 件のコメント:
コメントを投稿