Tetris3D
Tetris3D
* 昔WCANで使ったネタをちょっといじった
* http://blog.eternitydesign.net/archives/2009/05/tetris3d.html
*
* 180秒で何個ブロックを消せるか
* 対象ブロックは最下段の縦横5個
* (斜めは判定ないです)
*
*
* ・操作方法
*
* Spaceでゲーム開始
*
* I : ブロックを奥へ移動(Z+)
* M : ブロック手前へ移動(Z-)
* L : ブロックを右へ移動(X+)
* J : ブロックを左へ移動(X-)
* K : 早く落とす
*
* コツはちょい押しで移動
* 散らかったら、一度ブロック使って掃除しちゃうのもあり
*
/**
* Copyright eternity_hiro ( http://wonderfl.net/user/eternity_hiro )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/wLvS
*/
/**
* Tetris3D
* 昔WCANで使ったネタをちょっといじった
* http://blog.eternitydesign.net/archives/2009/05/tetris3d.html
*
* 180秒で何個ブロックを消せるか
* 対象ブロックは最下段の縦横5個
* (斜めは判定ないです)
*
*
* ・操作方法
*
* Spaceでゲーム開始
*
* I : ブロックを奥へ移動(Z+)
* M : ブロック手前へ移動(Z-)
* L : ブロックを右へ移動(X+)
* J : ブロックを左へ移動(X-)
* K : 早く落とす
*
* コツはちょい押しで移動
* 散らかったら、一度ブロック使って掃除しちゃうのもあり
*
*/
package {
import com.bit101.components.PushButton;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Vector3D;
import flash.net.URLRequest;
import flash.net.navigateToURL;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.Timer;
import flash.utils.escapeMultiByte;
import jiglib.physics.PhysicsSystem;
import jiglib.physics.RigidBody;
import jiglib.plugin.papervision3d.Papervision3DPhysics;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.render.QuadrantRenderEngine;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.Viewport3D;
[SWF(backgroundColor="#2e3036", frameRate="30", width="465", height="465")]
public class Tetris3d extends BasicView {
private var birdView:Viewport3D;
private var birdViewCamera:Camera3D;
private var tetrisStage:DisplayObject3D;
//private var tetrisCtrl:DO3DController;
private var physics:Papervision3DPhysics;
private var ground:RigidBody;
private var light:PointLight3D;
private var tetrisBlock:TetrisBlock;
private var dCubeNoText:TextField;
private var textFormat:TextFormat;
private var timer:Timer;
private var completeText:TextField;
private var countDown:TextField;
private const GAME_TIME:uint = 180;
public function Tetris3d() {
super(stage.stageWidth, stage.stageHeight, true, false);
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
// 俯瞰のため
birdView = new Viewport3D(100, 100);
birdViewCamera = new Camera3D();
birdViewCamera.rotationX = 90;
birdViewCamera.y = 800;
birdViewCamera.z = 0;
addChild(birdView);
tetrisStage = new DisplayObject3D();
scene.addChild(tetrisStage);
physics = new Papervision3DPhysics(tetrisStage, 5);
physics.engine.setGravity(new Vector3D(0, -2, 0));
light = new PointLight3D(true, true);
light.x = 0;
light.y = 400;
light.z = -300;
camera.y = 450;
camera.z = -500;
var materiaList:MaterialsList = new MaterialsList();
materiaList.addMaterial(new FlatShadeMaterial(light, 0xCCCCCC), "all");
// 床生成
ground = physics.createCube(materiaList, 200, 200, 5);
ground.movable = false;
ground.friction = 0.2;
ground.restitution = 0.8;
textFormat = new TextFormat("_sans", 10, 0xAAAAAA);
textFormat.letterSpacing = 1;
dCubeNoText = new TextField();
dCubeNoText.autoSize = TextFieldAutoSize.LEFT;
dCubeNoText.defaultTextFormat = textFormat;
dCubeNoText.x = 30;
dCubeNoText.y = 300;
addChild(dCubeNoText);
countDown = new TextField();
countDown.autoSize = TextFieldAutoSize.LEFT;
countDown.defaultTextFormat = textFormat;
countDown.text = "残り" + GAME_TIME + "秒";
countDown.x = 30;
countDown.y = 330;
addChild(countDown);
//renderer = new QuadrantRenderEngine(QuadrantRenderEngine.ALL_FILTERS);
stage.addEventListener(KeyboardEvent.KEY_DOWN, start);
}
private function start(e:KeyboardEvent):void {
trace("start");
if(e.keyCode == 32) {
stage.removeEventListener(KeyboardEvent.KEY_DOWN, start);
startRendering();
timer = new Timer(1000, GAME_TIME);
timer.addEventListener(TimerEvent.TIMER, timerEvent);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerComplete);
timer.start();
tetrisBlock = new TetrisBlock(physics, tetrisStage);
tetrisBlock.addEventListener("FALL_COMPLETE", checkFall)
tetrisBlock.createBlock();
stage.addEventListener(KeyboardEvent.KEY_DOWN, tetrisBlock.blockKeyEvent);
}
}
private function checkFall(e:Event):void {
trace("checkFall");
tetrisBlock.createBlock();
dCubeNoText.text = "消したCubeの数 : " + tetrisBlock.totalDestroyCube;
}
private function timerEvent(e:TimerEvent):void {
countDown.text = "残り" + (GAME_TIME - timer.currentCount) + "秒";
}
private function timerComplete(e:TimerEvent):void {
trace("Timer Complete!!!!");
stopRendering();
textFormat = new TextFormat("_sans", 28, 0xff0000);
textFormat.letterSpacing = 1;
var tweetBtn:PushButton = new PushButton(this, 10, 10, "Tweet Result", tweetBtnClickEvent);
tweetBtn.x = (stage.stageWidth - tweetBtn.width) * 0.5;
tweetBtn.y = (stage.stageHeight - tweetBtn.height) * 0.5 + 35;
completeText = new TextField();
completeText.autoSize = TextFieldAutoSize.LEFT;
completeText.defaultTextFormat = textFormat;
completeText.text = "GAME OVER";
completeText.x = (stage.stageWidth - completeText.width) * 0.5;
completeText.y = (stage.stageHeight - completeText.height) * 0.5;
addChild(completeText);
}
private function tweetBtnClickEvent(e:MouseEvent):void {
navigateToURL(new URLRequest("http://twitter.com/home/?status=" + escapeMultiByte("#tetris3d 消したCubeの数 : " + tetrisBlock.totalDestroyCube + " http://wonderfl.net/c/wLvS/")));
}
protected override function onRenderTick(e:Event = null):void {
PhysicsSystem.getInstance().integrate(0.2);
renderer.renderScene(scene, camera, viewport);
renderer.renderScene(scene, birdViewCamera, birdView);
}
}
}
import com.adobe.utils.ArrayUtil;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.geom.Vector3D;
import jiglib.geometry.*;
import jiglib.math.*;
import jiglib.physics.*;
import jiglib.physics.constraint.*;
import jiglib.plugin.papervision3d.*;
import org.papervision3d.core.proto.DisplayObjectContainer3D;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.shadematerials.*;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cube;
class TetrisBlock extends Sprite {
private var physics:Papervision3DPhysics;
private var target:DisplayObjectContainer3D;
private var light:PointLight3D;
private var cubeML:MaterialsList;
private var ckFlg:Number;
private var ckAry:Array = new Array();
private var jboxAry:Array;
private var blockAry:Array;
private var posAry:Array;
private var massNum:Number = 5;
public var totalDestroyCube:Number = 0;
public function TetrisBlock(pp:Papervision3DPhysics, scene:DisplayObjectContainer3D) {
physics = pp;
target = scene;
jboxAry = new Array();
blockAry = new Array();
posAry = new Array(-40, 0, 40);
light = new PointLight3D(true, true);
light.x = 0;
light.y = 400;
light.z = -300;
}
public function createBlock():void {
var num:Number = MathUtil.decimalRandomRange(0, 3);
trace("num : " + num);
switch(num) {
case 1:
createBlock3();
break;
case 2:
createBlock2();
break;
case 3:
createBlock4();
break;
default:
createBlock1();
}
//createBlockDebug2();
}
private function createBlock1():void {
cubeML = new MaterialsList();
cubeML.addMaterial(new FlatShadeMaterial(light, 0xFF9912), "all");
blockAry = [];
ckFlg = 0;
var xPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
var zPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
trace(zPos);
for(var i:uint = 0; i < 4; i++) {
blockAry[i] = createCube(cubeML, 40, 40, 40);
blockAry[i].x = xPos;
blockAry[i].y = 400 + (40 * i);
blockAry[i].z = zPos;
ckAry[i] = false;
}
addEventListener(Event.ENTER_FRAME, checkFallEnd);
}
private function createBlock2():void {
cubeML = new MaterialsList();
cubeML.addMaterial(new FlatShadeMaterial(light, 0x0DB8FF), "all");
blockAry = [];
ckAry = [];
ckFlg = 0;
var xPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
var zPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
trace(zPos);
blockAry[0] = createCube(cubeML, 40, 40, 40);
blockAry[0].x = xPos;
blockAry[0].y = 400;
blockAry[0].z = zPos;
ckAry[0] = false;
blockAry[1] = createCube(cubeML, 40, 40, 40);
blockAry[1].x = xPos - 40;
blockAry[1].y = 400;
blockAry[1].z = zPos;
ckAry[1] = false;
blockAry[2] = createCube(cubeML, 40, 40, 40);
blockAry[2].x = xPos + 40;
blockAry[2].y = 400;
blockAry[2].z = zPos;
ckAry[2] = false;
addEventListener(Event.ENTER_FRAME, checkFallEnd);
}
private function createBlock3():void {
cubeML = new MaterialsList();
cubeML.addMaterial(new FlatShadeMaterial(light, 0x0AFF26), "all");
blockAry = [];
ckAry = [];
ckFlg = 0;
var xPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
var zPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
trace(zPos);
blockAry[0] = createCube(cubeML, 40, 40, 40);
blockAry[0].x = xPos;
blockAry[0].y = 400;
blockAry[0].z = zPos;
ckAry[0] = false;
blockAry[1] = createCube(cubeML, 40, 40, 40);
blockAry[1].x = xPos - 40;
blockAry[1].y = 400;
blockAry[1].z = zPos;
ckAry[1] = false;
blockAry[2] = createCube(cubeML, 40, 40, 40);
blockAry[2].x = xPos;
blockAry[2].y = 400;
blockAry[2].z = zPos + 40;
ckAry[2] = false;
blockAry[3] = createCube(cubeML, 40, 40, 40);
blockAry[3].x = xPos - 40;
blockAry[3].y = 400;
blockAry[3].z = zPos + 40;
ckAry[3] = false;
addEventListener(Event.ENTER_FRAME, checkFallEnd);
}
private function createBlock4():void {
cubeML = new MaterialsList();
cubeML.addMaterial(new FlatShadeMaterial(light, 0xFF0CF5), "all");
blockAry = [];
ckAry = [];
ckFlg = 0;
var xPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
var zPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
trace(zPos);
blockAry[0] = createCube(cubeML, 40, 40, 40);
blockAry[0].x = xPos;
blockAry[0].y = 400;
blockAry[0].z = zPos;
ckAry[0] = false;
blockAry[1] = createCube(cubeML, 40, 40, 40);
blockAry[1].x = xPos + 40;
blockAry[1].y = 400;
blockAry[1].z = zPos;
ckAry[1] = false;
blockAry[2] = createCube(cubeML, 40, 40, 40);
blockAry[2].x = xPos;
blockAry[2].y = 400;
blockAry[2].z = zPos + 40;
ckAry[2] = false;
addEventListener(Event.ENTER_FRAME, checkFallEnd);
}
//---------------------------------------
// X軸方向5個Cube
//---------------------------------------
private function createBlockDebug():void {
cubeML = new MaterialsList();
cubeML.addMaterial(new FlatShadeMaterial(light, 0xFF0E0B), "all");
blockAry = [];
ckAry = [];
ckFlg = 0;
var xPos:Number = 0;
var zPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
trace(zPos);
blockAry[0] = createCube(cubeML, 40, 40, 40);
blockAry[0].x = xPos;
blockAry[0].y = 400;
blockAry[0].z = zPos;
ckAry[0] = false;
blockAry[1] = createCube(cubeML, 40, 40, 40);
blockAry[1].x = xPos - 40;
blockAry[1].y = 400;
blockAry[1].z = zPos;
ckAry[1] = false;
blockAry[2] = createCube(cubeML, 40, 40, 40);
blockAry[2].x = xPos + 40;
blockAry[2].y = 400;
blockAry[2].z = zPos;
ckAry[2] = false;
blockAry[3] = createCube(cubeML, 40, 40, 40);
blockAry[3].x = xPos - 80;
blockAry[3].y = 400;
blockAry[3].z = zPos;
ckAry[3] = false;
blockAry[4] = createCube(cubeML, 40, 40, 40);
blockAry[4].x = xPos + 80;
blockAry[4].y = 400;
blockAry[4].z = zPos;
ckAry[4] = false;
addEventListener(Event.ENTER_FRAME, checkFallEnd);
}
//---------------------------------------
// Z軸方向5個Cube
//---------------------------------------
private function createBlockDebug2():void {
cubeML = new MaterialsList();
cubeML.addMaterial(new FlatShadeMaterial(light, 0xFF0E0B), "all");
blockAry = [];
ckAry = [];
ckFlg = 0;
var xPos:Number = posAry[MathUtil.decimalRandomRange(0, 2)];
var zPos:Number = 0;
trace(zPos);
blockAry[0] = createCube(cubeML, 40, 40, 40);
blockAry[0].x = xPos;
blockAry[0].y = 400;
blockAry[0].z = zPos;
ckAry[0] = false;
blockAry[1] = createCube(cubeML, 40, 40, 40);
blockAry[1].x = xPos;
blockAry[1].y = 400;
blockAry[1].z = zPos - 40;
ckAry[1] = false;
blockAry[2] = createCube(cubeML, 40, 40, 40);
blockAry[2].x = xPos;
blockAry[2].y = 400;
blockAry[2].z = zPos + 40;
ckAry[2] = false;
blockAry[3] = createCube(cubeML, 40, 40, 40);
blockAry[3].x = xPos;
blockAry[3].y = 400;
blockAry[3].z = zPos - 80;
ckAry[3] = false;
blockAry[4] = createCube(cubeML, 40, 40, 40);
blockAry[4].x = xPos;
blockAry[4].y = 400;
blockAry[4].z = zPos + 80;
ckAry[4] = false;
addEventListener(Event.ENTER_FRAME, checkFallEnd);
}
//---------------------------------------
// ブロック移動用キーイベント
//---------------------------------------
public function blockKeyEvent(e:KeyboardEvent):void {
trace("keyDownHandler2: " + e.keyCode);
switch(e.keyCode) {
case 73:
// i
addWorldForceZ(40);
break;
case 77:
// m
addWorldForceZ(-40);
break;
case 76:
// l
addWorldForceX(40);
break;
case 74:
// j
addWorldForceX(-40);
break;
case 75:
// k
addWorldForceY(-90);
break;
}
}
private function addWorldForceX(n:Number):void {
for(var i:uint = 0; i < blockAry.length; i++) {
blockAry[i].addWorldForce(new Vector3D(n, 0, 0), blockAry[i].currentState.position);
}
}
private function addWorldForceZ(n:Number):void {
for(var i:uint = 0; i < blockAry.length; i++) {
blockAry[i].addWorldForce(new Vector3D(0, 0, n), blockAry[i].currentState.position);
}
}
private function addWorldForceY(n:Number):void {
for(var i:uint = 0; i < blockAry.length; i++) {
blockAry[i].addWorldForce(new Vector3D(0, n, 0), blockAry[i].currentState.position);
}
}
private function addWorldTorqueY(n:Number):void {
for(var i:uint = 0; i < blockAry.length; i++) {
blockAry[i].addWorldTorque(new Vector3D(0, n, 0));
}
}
public function createCube(materials:MaterialsList, width:Number=500, depth:Number=500, height:Number=500, segmentsS:int=1, segmentsT:int=1, segmentsH:int=1, insideFaces:int=0, excludeFaces:int=0):RigidBody {
var cube:Cube = new Cube(materials, width, depth, height, segmentsS, segmentsT, segmentsH, insideFaces, excludeFaces);
target.addChild(cube);
var jbox:JBox = new JBox(new Pv3dMesh(cube), width, depth, height);
jbox.restitution = 0; // Default = 0.2
jbox.friction = 1; // Default = 0.5
physics.addBody(jbox);
jboxAry.push(jbox);
return jbox;
}
public function destroyCube(n:Number):void {
target.removeChild(physics.getMesh(jboxAry[n]));
physics.removeBody(jboxAry[n]);
jboxAry[n] = null;
}
//---------------------------------------
// 一番新しいブロックのCubeすべての停止を判定
//---------------------------------------
private function checkFallEnd(e:Event):void {
ckAry = [];
for(var i:uint = 0; i < blockAry.length; i++) {
if(blockAry[i].currentState.linVelocity.x == 0 && blockAry[i].currentState.linVelocity.y == 0 && blockAry[i].currentState.linVelocity.z == 0) {
ckAry[i] = true;
}
if(blockAry[i].y < -200) {
ckAry[i] = true;
}
}
for(i = 0; i < blockAry.length; i++) {
if(ckAry[i] == true) {
ckFlg++;
}
}
if(ckFlg >= blockAry.length) {
trace("MOVE COMPLETE!!!");
removeEventListener(Event.ENTER_FRAME, checkFallEnd);
checkAllCube();
setAllCubeActive();
dispatchEvent(new Event("FALL_COMPLETE"));
}
ckFlg = 0;
}
//---------------------------------------
// 消去対象のCubeの精査と消去
// なんかもっといいコード誰か考えて!!
//---------------------------------------
private function checkAllCube():void {
var destroyCubeAry:Array = new Array();
var lowestAry:Array = new Array();
var destroyCubeNo:Number = 0;
var xStart:Number = -100;
var zStart:Number = -100;
// フラグの初期化
var destroyCubeFlhAry:Array = new Array();
for(var i:uint = 0; i < jboxAry.length; i++) {
destroyCubeFlhAry[i] = false;
}
// マス用配列の初期化
for(var j:Number = 0; j < massNum; j++) {
var tmpAry:Array = new Array();
for(var k:Number = 0; k < massNum; k++) {
tmpAry[k] = null;
//lowestAry[j][k] = false;
}
lowestAry[j] = tmpAry;
}
// まずは全Cubeから最下段にあるCubeを検索し、指定されたマス内にあるCubeを2次元配列に格納
for(i = 0; i < jboxAry.length; i++) {
if(jboxAry[i] != null) {
// X軸Z軸で5個並んでるところのチェック(一番下の段だけ)
if(jboxAry[i].y <= 40 && jboxAry[i].y >= 0) {
for(j = 0; j < massNum; j++) {
if(jboxAry[i].x >= xStart && jboxAry[i].x <= (xStart + 40)) {
zStart = -100;
for(k = 0; k < massNum; k++) {
if(jboxAry[i].z >= zStart && jboxAry[i].z <= (zStart + 40)) {
lowestAry[j][k] = i;
}
zStart += 40;
}
}
xStart += 40;
}
xStart = -100;
}
// 下に落ちてるCubeを削除
if(jboxAry[i].y < -100) {
trace("jboxAry.y : " + jboxAry[i].y);
destroyCube(i);
}
}
}
// 上記で作成された2次元配列から5個揃っている列をX、Z軸双方からサーチして削除対象用配列に格納
for(j = 0; j < massNum; j++) {
var chkFlg:Number = 0;
for(k = 0; k < massNum; k++) {
if(lowestAry[j][k] != null) {
chkFlg++;
}
}
// Z軸方向にサーチして5個揃ってたら削除対象配列にpush
if(chkFlg == 5) {
for(k = 0; k < massNum; k++) {
destroyCubeAry.push(lowestAry[j][k]);
}
}
chkFlg = 0;
}
for(k = 0; k < massNum; k++) {
chkFlg = 0;
for(j = 0; j < massNum; j++) {
if(lowestAry[j][k] != null) {
chkFlg++;
}
}
// X軸方向にサーチして5個揃ってたら削除対象配列にpush
if(chkFlg == 5) {
for(j = 0; j < massNum; j++) {
destroyCubeAry.push(lowestAry[j][k]);
}
}
chkFlg = 0;
}
// 重複要素の削除
destroyCubeAry = ArrayUtil.createUniqueCopy(destroyCubeAry);
// ここで配列に入ってるものを削除
for(i = 0; i < destroyCubeAry.length; i++) {
//trace("destroyCubeAry.y : " + destroyCubeAry[i].y);
totalDestroyCube++;
destroyCube(destroyCubeAry[i]);
}
}
//---------------------------------------
// 残ってるjBoxをすべてActiveに
//---------------------------------------
private function setAllCubeActive():void {
for(var i:uint = 0; i < jboxAry.length; i++) {
if(jboxAry[i] != null) {
jboxAry[i].setActive();
}
}
}
}
class MathUtil {
public static function decimalRandomRange(min:Number, max:Number, digit:uint = 0):Number {
var randomNum:Number = Math.random() * (max - min) + min;
return decimalRound(randomNum, digit);
}
public static function decimalRound(i:Number , digit:uint):Number {
return Math.round(i * Math.pow(10 , digit)) / Math.pow(10 , digit);
}
}