へたれゲーム

flash暦10日。

TamaGiri.as

/*============================================================
 * File: TamaGiri.as
 * About: TamaGiri game controller
 */

class TamaGiri
{
    private var root:MovieClip;
    private var canvas:MovieClip;
    private var CANVAS_DEPTH:Number = 100;
    private var TAMA_DEPTH:Number = 200;
    private var scoreField:TextField;
    private var SCORE_FIELD_DEPTH:Number = 101;
    private var lifeField:TextField;
    private var LIFE_FIELD_DEPTH:Number = 102;
    private var over:TextField;
    private var OVER_DEPTH:Number = 103;
    private var continue_button:MovieClip;
    private var CONTINUE_DEPTH:Number = 10400;
    private var titleField:TextField;
    private var TITLE_FIELD_DEPTH:Number = 105;
    private var start_button:MovieClip;
    private var START_DEPTH:Number = 10500;
    private var to_title_button:MovieClip;
    private var TO_TITLE_BUTTON_DEPTH:Number = 10600;
    private var bomField:TextField;
    private var BOM_FIELD_DEPTH:Number = 107;
    private var mySound:Sound;
    private var mySound2:Sound;
    private var tamaList:Array;
    private var GAME_OVER_TAMA_AWAY:Number = 10;

    private var BOM_ENEMY:Number = 10;



    private var is_mouse_pressed:Boolean;
    private var last_x:Number;
    private var last_y:Number;
    private var last_date_num:Number;

    private var tamaCutTotal:Number;
    private var tamaIndex:Number;
    private var tamaAwayTotal:Number;
    private var bom:Number;
    
    function TamaGiri(parent:MovieClip)
    {
	root = parent;
	init();
	init_op();
    }

    function init() {
	over = undefined;
	continue_button = undefined;
	titleField = undefined;
	start_button = undefined;
	root.createEmptyMovieClip("canvas", CANVAS_DEPTH);
	canvas = root["canvas"];
	tamaList = new Array();
	setupEventListener();
	is_mouse_pressed = false;
	last_x = 0;
	last_y = 0;
	var last_date:Date = new Date();
	last_date_num = last_date.valueOf();
	tamaCutTotal = 0;
	tamaIndex = 0;
	tamaAwayTotal = 0;
	bom = 0;

	Tama.sum = 0;

	var format:TextFormat = new TextFormat();
	format.size = 30;	

	// bom
	canvas.createTextField("bom", BOM_FIELD_DEPTH,
			       Stage.width-600,
			       Stage.height-50,
			       100,40);
	bomField = canvas["bom"];
	bomField.autoSize = true;
	bomField.setNewTextFormat(format);
	bomField.text = "bom: " + Math.floor(bom / BOM_ENEMY);

	// score
 	canvas.createTextField("score", SCORE_FIELD_DEPTH,
			       Stage.width-400,
			       Stage.height-50,
			       200,40);
	scoreField = canvas["score"];
	scoreField.autoSize = true;
	scoreField.setNewTextFormat(format);
	scoreField.text = "SCORE: " + tamaCutTotal;

	//life
 	canvas.createTextField("life", LIFE_FIELD_DEPTH,
			       Stage.width-150,
			       Stage.height-50,
			       100,40);
	lifeField = canvas["life"];
	lifeField.autoSize = true;
	lifeField.setNewTextFormat(format);
	lifeField.text = "LIFE: " + (GAME_OVER_TAMA_AWAY - tamaAwayTotal);
	

    }
    function init_op()
    {
	setupOpenningEventListener();
	var format:TextFormat = new TextFormat();
	format.size = 100;
 	canvas.createTextField("title", TITLE_FIELD_DEPTH,
			       Stage.width/2-100,
			       Stage.height/2-100,
			       200,40);
	titleField = canvas["title"];
	titleField.autoSize = "center";
	titleField.setNewTextFormat(format);
	titleField.text = "Tama Giri !!";

	var loader:MovieClipLoader = new MovieClipLoader();
	var loader_event_object:Object = new Object;
	canvas.createEmptyMovieClip("start", START_DEPTH);
	start_button = canvas["start"];
	loader_event_object.info = {x:Stage.width/2-100,
				    y:Stage.height/2+100};
	var self:TamaGiri = this;
	loader_event_object.onLoadComplete = function(mc:MovieClip) {
	    mc._x = this.info.x;
	    mc._y = this.info.y;
	    mc.onRelease = function() {
 		self.init();
 	    };
	};
	loader.addListener(loader_event_object);
	loader.loadClip("img/start.jpg", start_button);
    }

    function setupOpenningEventListener() {
	var self:TamaGiri = this;
	root.onEnterFrame = function() {
	    self.onOpenningEnterFrameHandler();
	};
    }

    function setupEventListener() {
	var self:TamaGiri = this;
	root.onEnterFrame = function() {
	    self.onEnterFrameHandler();
	};
	root.onMouseDown = function() {
	    self.onMouseDownHandler();
	};
	root.onMouseUp = function() {
	    self.onMouseUpHandler();
	};
	root.onReleaseOutside = function() {
	    self.onReleaseOutsideHandler();
	};
	root.onRollOut = function() {
	    self.onRollOutHandler();
	};
    }

    function onOpenningEnterFrameHandler() :Void
    {
	if (tamaList.length < 20) {
	    if (probability(5)) {
		canvas.createEmptyMovieClip("tama" + tamaIndex,
					    TAMA_DEPTH+tamaIndex);
		tamaList.push(new Tama(canvas["tama" + tamaIndex]));
		tamaIndex++;
	    }
	}
	// 玉の走査
	var newTamaList:Array = new Array();
	for(var i:Number = 0; i < tamaList.length; i++) {

	    // 削除
	    if (! tamaList[i].isVisible()) {
		if (tamaList[i].status() == Tama.TAMA_DOWN) {
		    tamaAwayTotal++;
		}
		tamaList[i].remove();
		delete tamaList[i];
	    } else  {
		// hit test
		if (is_mouse_pressed &&
		    tamaList[i].status() == Tama.TAMA_DOWN &&
		    tamaList[i].isHit(last_x,last_y,
				      root._xmouse,root._ymouse)) {
		    tamaCutTotal++;
		    bom++;
		}
		// 次回も利用
		newTamaList.push(tamaList[i]);
	    }
	}
	tamaList = newTamaList;
	last_x=root._xmouse;
	last_y=root._ymouse;

	// スコア、ライフ表示
	scoreField.text = "SCORE: " + tamaCutTotal;
	lifeField.text = "LIFE: " + (GAME_OVER_TAMA_AWAY - tamaAwayTotal);
	bomField.text = "bom: " + Math.floor(bom / BOM_ENEMY);
    }
    

    function onEnterFrameHandler() :Void
    {
	// 玉の生成
	if (tamaList.length < (Math.floor(tamaCutTotal/5) + 1)) {
	    if (probability(5)) {
		canvas.createEmptyMovieClip("tama" + tamaIndex,
					    TAMA_DEPTH+tamaIndex);
		tamaList.push(new Tama(canvas["tama" + tamaIndex]));
		tamaIndex++;
	    }
	}

	// 玉の走査
	var newTamaList:Array = new Array();
	for(var i:Number = 0; i < tamaList.length; i++) {

	    // 削除
	    if (! tamaList[i].isVisible()) {
		if (tamaList[i].status() == Tama.TAMA_DOWN) {
		    tamaAwayTotal++;
		}
		tamaList[i].remove();
		delete tamaList[i];
	    } else  {
		// hit test
		if (is_mouse_pressed &&
		    tamaList[i].status() == Tama.TAMA_DOWN &&
		    tamaList[i].isHit(last_x,last_y,
				      root._xmouse,root._ymouse)) {
		    tamaCutTotal++;
		    bom++;
		    if (tamaCutTotal % 20 == 0) {
			tamaAwayTotal--;
		    }
		}
		// 次回も利用
		newTamaList.push(tamaList[i]);
	    }
	}
	tamaList = newTamaList;
	last_x=root._xmouse;
	last_y=root._ymouse;

	// スコア、ライフ表示
	scoreField.text = "SCORE: " + tamaCutTotal;
	lifeField.text = "LIFE: " + (GAME_OVER_TAMA_AWAY - tamaAwayTotal);
	bomField.text = "bom: " + Math.floor(bom / BOM_ENEMY);

	// ゲームオーバー
	if (tamaAwayTotal >= GAME_OVER_TAMA_AWAY) {
	    var self:TamaGiri = this;
	    for(var i:Number = 0; i < tamaList.length; i++) {
		tamaList[i].gameOver();
	    }
	    tamaList = undefined;
	    root.onEnterFrame = function () {
		self.onGameOverFrame();
	    }
	    // game over
	}
    }

    function onGameOverEnterFrameHandler() :Void
    {
	if (tamaList.length < 20) {
	    if (probability(5)) {
		canvas.createEmptyMovieClip("tama" + tamaIndex,
					    TAMA_DEPTH+tamaIndex);
		tamaList.push(new Tama(canvas["tama" + tamaIndex]));
		tamaIndex++;
	    }
	}
	// 玉の走査
	var newTamaList:Array = new Array();
	for(var i:Number = 0; i < tamaList.length; i++) {

	    // 削除
	    if (! tamaList[i].isVisible()) {
		tamaList[i].remove();
		delete tamaList[i];
	    } else  {
		// hit test
		if (is_mouse_pressed &&
		    tamaList[i].status() == Tama.TAMA_DOWN &&
		    tamaList[i].isHit(last_x,last_y,
				      root._xmouse,root._ymouse)) {
		    bom++;
		}
		// 次回も利用
		newTamaList.push(tamaList[i]);
	    }
	}
	tamaList = newTamaList;
	last_x=root._xmouse;
	last_y=root._ymouse;

	// スコア、ライフ表示
	scoreField.text = "SCORE: " + tamaCutTotal;
	lifeField.text = "LIFE: " + (GAME_OVER_TAMA_AWAY - tamaAwayTotal);
    }

    function onGameOverFrame() :Void
    {
	canvas.createTextField("over", OVER_DEPTH,
			       Stage.width/2-200, Stage.height/2-40,
			       200, 40);
	over = canvas["over"];
	var format:TextFormat = new TextFormat();
	format.size = 50;
	over.setNewTextFormat(format);
	over.text = "GAME OVER";
	over.autoSize = "center";

	var self:TamaGiri = this;

	// continue
	var loader:MovieClipLoader = new MovieClipLoader();
	var loader_event_object:Object = new Object;
	canvas.createEmptyMovieClip("continue", CONTINUE_DEPTH);
	continue_button = canvas["continue"];
	loader_event_object.info = {x:Stage.width/2+75,
				    y:Stage.height/2-31};
	loader_event_object.onLoadComplete = function(mc:MovieClip) {
	    mc._x = this.info.x;
	    mc._y = this.info.y;
	    mc.onRelease = function() {
 		self.init();
 	    };
	};
	loader.addListener(loader_event_object);
	loader.loadClip("img/continue.jpg", continue_button);

	// to title
	var loader_a:MovieClipLoader = new MovieClipLoader();
	var loader_event_object_a:Object = new Object;
	canvas.createEmptyMovieClip("to_title", TO_TITLE_BUTTON_DEPTH);
	to_title_button = canvas["to_title"];
	loader_event_object_a.info = {x:Stage.width/2+75,
				      y:Stage.height/2+20};
	loader_event_object_a.onLoadComplete = function(mc:MovieClip) {
	    mc._x = this.info.x;
	    mc._y = this.info.y;
	    mc.onRelease = function() {
 		self.init();
		self.init_op();
 	    };
	};
	loader_a.addListener(loader_event_object_a);
	loader_a.loadClip("img/title.jpg", to_title_button);

	tamaList = new Array();
	root.onEnterFrame = function () {
	    self.onGameOverEnterFrameHandler();
	};
    }

    function onMouseDownHandler() :Void
    {
	last_x=root._xmouse;
	last_y=root._ymouse;

	var now:Date = new Date();
	var now_num:Number = now.valueOf();
	if (now_num - last_date_num < 1000) {
	    if (bom - BOM_ENEMY >= 0) {
		bom -= BOM_ENEMY;
		for(var i:Number = 0; i < tamaList.length; i++) {
		    tamaList[i].forceHit();
		}
		now_num -= 2000;
	    }
	}
	last_date_num = now_num;
	is_mouse_pressed = true;
    }
    function onMouseUpHandler() :Void
    {
	is_mouse_pressed = false;
    }
    function onReleaseOutsideHandler() :Void
    {
	is_mouse_pressed = false;
    }
    function onRollOutHandler() :Void
    {
	is_mouse_pressed = false;
    }

    // [p]%の確立でtrueを返す
    private function probability(p:Number) :Boolean
    {
	return Math.random() < (p / 100);
    }

    static function main() :Void
    {
	var base:TamaGiri = new TamaGiri(_root);
    }
}

Tama.as

/*============================================================
 * File: Tama.as
 * About: Tama class
 */

class Tama
{
    static var TAMA_DOWN:Number = 0;
    static var TAMA_HIT:Number = 1;
    static var TAMA_HIT_FORCE:Number = 2;

    static var sum: Number = 0;

    private var mc :MovieClip;
    private var state :Number;
    private var TAMA_STATE_ALIVE  :Number = 0;
    private var TAMA_STATE_TO_DIE :Number = 1;
    private var TAMA_STATE_DIE    :Number = 2;
    private var TAMA_R :Number = 30;
    private var TAMA_MARGIN:Number = 40;
    private var COLOR_BLACK:Number = 0x000000;
    private var speed:Number = 1;
    private var g:Number = 9.8;
    private var div:Number = 24;

    private var cycle_flag:Number;

    private var last_x:Number;
    private var last_y:Number;

    static function col( t )
    {
	var tx = Math.PI*2/3;
	while( Math.PI*2<t ){
	    t-=Math.PI*2;
	}
	if ( 0<=t && t<=tx ){
	    return 0;
	}
	return Math.floor( 128-Math.cos((t-tx)*1.5)*127 );
    }

    static function getColor() :Number
    {
	var t:Number = sum;
	var rgb = col(t)*0x10000 + col(t+Math.PI*2/3)*0x100 + col(t+Math.PI*4/3);
	sum++;
	return rgb;
    }

    function Tama(_mc :MovieClip)
    {
	state = Tama.TAMA_DOWN;
	mc = _mc;
	mc._y = -TAMA_R;
	mc._x = downPoint();

	last_y = mc._y;
	last_x = mc._x;

	// 	mc.lineStyle(0, COLOR_BLACK);
	// 	mc.beginFill(COLOR_BLACK);
	var color:Number = getColor();
 	mc.lineStyle(0, color);
 	mc.beginFill(color);

	// form
	if (probability(30)) {
	    drawCircle(mc, 0,0, TAMA_R);
	} else if (probability(60)) {
	    drawSquare(mc, 0, 0, TAMA_R*2);
	} else {
	    drawTriangle(mc,0,0, TAMA_R*1.5);
	}
	mc.endFill();

	// rotation
	if (probability(50)) {
	    cycle_flag = 1;
	} else {
	    cycle_flag = -1;
	}

	var self:Tama = this;
	mc.onEnterFrame = function() {
	    self.down();
	};
    }

    function remove()
    {
	mc.removeMovieClip();
    }

    private function downPoint() :Number
    {
	return  Math.random() * (Stage.width - 2*TAMA_MARGIN) + TAMA_MARGIN;
    }

    function down()
    {
	last_x = mc._x;
	last_y = mc._y;
	// 下まで行ききった
	if (mc._y > Stage.height + TAMA_R) {
	    mc._visible = false;
	}
	mc._rotation = mc._rotation + cycle_flag * 5;
	speed = speed + g / div;
	mc._y = (mc._y + speed * 1);
    }

    function hidding() :Void
    {
	if (mc._alpha < 1) {
	    mc._visible = false;
	} else {
	    mc._alpha = mc._alpha * 0.75;
	}
    }
    function gameOver() :Void
    {
	mc.stop();
    }

    function isVisible():Boolean
    {
	return mc._visible;
    }

    function status():Number
    {
	return state;
    }

    private function isHit(x1:Number, y1:Number, x2:Number, y2:Number):Boolean
    {
	if (mc.hitTest(x1,y2, true)) {
	    state = Tama.TAMA_HIT;
	    var self:Tama = this;
	    mc.onEnterFrame = function() {
		self.hidding();
	    };
	    return true;
	}
	return false;
    }

    function forceHit() :Void
    {
	state = Tama.TAMA_HIT_FORCE;
	var self:Tama = this;
	mc.onEnterFrame = function() {
	    self.hidding();
	};
    }

    // 円を描く
    function drawCircle(mc:MovieClip, x:Number, y:Number, r:Number):Void
    {
	mc.moveTo(x+r, y);
	mc.curveTo(r+x, Math.tan(Math.PI/8)*r+y, Math.sin(Math.PI/4)*r+x,
		   Math.sin(Math.PI/4)*r+y);
	mc.curveTo(Math.tan(Math.PI/8)*r+x, r+y, x, r+y);
	mc.curveTo(-Math.tan(Math.PI/8)*r+x, r+y, -Math.sin(Math.PI/4)*r+x,
		   Math.sin(Math.PI/4)*r+y);
	mc.curveTo(-r+x, Math.tan(Math.PI/8)*r+y, -r+x, y);
	mc.curveTo(-r+x, -Math.tan(Math.PI/8)*r+y, -Math.sin(Math.PI/4)*r+x,
		   -Math.sin(Math.PI/4)*r+y);
	mc.curveTo(-Math.tan(Math.PI/8)*r+x, -r+y, x, -r+y);
	mc.curveTo(Math.tan(Math.PI/8)*r+x, -r+y, Math.sin(Math.PI/4)*r+x,
		   -Math.sin(Math.PI/4)*r+y);
	mc.curveTo(r+x, -Math.tan(Math.PI/8)*r+y, r+x, y);
    }

    // FIXME: draw
    function drawTriangle(mc:MovieClip, x:Number, y:Number, l:Number) :Void
    {
	var n:Number = 2 * l / Math.sqrt(3);
	mc.moveTo(x, y - (n*2/3) );
	mc.lineTo(x + l*Math.sqrt(2)/2, y + (n*1/3) );
	mc.lineTo(x - l*Math.sqrt(2)/2, y + (n*1/3) );
	mc.lineTo(x, y - (n*2/3) );
    }

    function drawSquare(mc:MovieClip, x:Number, y:Number, l:Number) :Void
    {
	mc.moveTo(x-l/2, y-l/2);
	mc.lineTo(x-l/2, y+l/2);
	mc.lineTo(x+l/2, y+l/2);
	mc.lineTo(x+l/2, y-l/2);
	mc.lineTo(x-l/2, y-l/2);
    }
    // [p]%の確立でtrueを返す
    private function probability(p:Number) :Boolean
    {
	return Math.random() < (p / 100);
    }
}

11/2-4は文化祭だったので、そのときwiiで動かして遊んだ。出し物の本命は山田ウイルスの感染実演だったので、これは脇役だったけど。
今一週間たって冷静にみると、ぼろぼろだな。以下反省点。

  • 三角形の描写は適当。正三角形ではない(汗
  • 色付けは参考にしたソースをコピペ、、
  • コピペばかり。リファクタリングしていない。
  • 状態遷移でコールバック関数を丸々入れ替えている。ソースを追わないと混乱する。