(仮)シロクロ×ダイバー(清書中)
正式版→http://wonderfl.net/c/e5y9
(これをForkせずに作ってしまった)
「シロクロダイバー」
・「空間」と「ブロック」が「境界」にて入れ替わるアクションゲーム(の試作)
・「しろくろニクル」でできなかったことのテスト
・色々と仮。
→あとでBox2Dと統合したりしたやつをUP予定。
操作方法
・十字キー:移動
・Rキー:リスタート(ゴール後のリスタートは未対応)
アルゴリズム
・移動方法は「簡易2Dアクションゲームテンプレート」に準拠
・Bitmapに何か描かれていれば地形とみなす
・「地形」を「ブロックの縁」とすることで、ブロックの中も移動できる
・「ブラー」+「元画像」で、ブラー部分を地形用の線として扱う
・「ブラー」をかけない部分は線が作られないので、そこからブロックの内外へ移動できる
次に作成する場合にやること
・Box2Dとの統合
・プレイヤーのアクションなどをBox2Dと統合する
・「Box2Dで次の目的地を決める→今のアルゴリズムで移動を試みる→到達状況に応じてBox2D側を修正」というループ
・「押せるブロック」の作成
・「黒いブロック」を白いプレイヤーの状態で押して、黒いプレイヤーの状態で通ったりしたい
・おそらくプレイヤーと同じような処理で上手くいくはず
/**
* Copyright o_healer ( http://wonderfl.net/user/o_healer )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/cRgx
*/
// forked from o_healer's 簡易2Dアクションゲームテンプレート
// forked from o_healer's お絵かきサンプル
//正式版→http://wonderfl.net/c/e5y9
//(これをForkせずに作ってしまった)
/*
「シロクロダイバー」
・「空間」と「ブロック」が「境界」にて入れ替わるアクションゲーム(の試作)
・「しろくろニクル」でできなかったことのテスト
・色々と仮。
→あとでBox2Dと統合したりしたやつをUP予定。
操作方法
・十字キー:移動
・Rキー:リスタート(ゴール後のリスタートは未対応)
アルゴリズム
・移動方法は「簡易2Dアクションゲームテンプレート」に準拠
・Bitmapに何か描かれていれば地形とみなす
・「地形」を「ブロックの縁」とすることで、ブロックの中も移動できる
・「ブラー」+「元画像」で、ブラー部分を地形用の線として扱う
・「ブラー」をかけない部分は線が作られないので、そこからブロックの内外へ移動できる
次に作成する場合にやること
・Box2Dとの統合
・プレイヤーのアクションなどをBox2Dと統合する
・「Box2Dで次の目的地を決める→今のアルゴリズムで移動を試みる→到達状況に応じてBox2D側を修正」というループ
・「押せるブロック」の作成
・「黒いブロック」を白いプレイヤーの状態で押して、黒いプレイヤーの状態で通ったりしたい
・おそらくプレイヤーと同じような処理で上手くいくはず
*/
package {
import flash.display.Sprite;
import flash.events.*;
public class FlashTest extends Sprite {
public function FlashTest() {
// write as3 code here..
addChild(new GameMain());
}
}
}
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.filters.*;
import flash.ui.Keyboard;
internal class GameMain extends Sprite{
//==Const==
static public const PANEL_LEN:int = 32;//16;
//マップ
static public const P:int = 99;
static public const G:int = 100;
static public const MAP:Array = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 2, 0, 1, 1, 1, 1, 0],
[1, P, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, G, 0, 1, 0, 2, 1, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 2, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
];
//マップのサイズ
static public var W:int = 100;//あとでちゃんと計算
static public var H:int = 100;
//==Var==
//#Layer
//- Root
public var m_Layer_Root:Sprite;
//-- 地形
public var m_Layer_Terrain:Sprite;
//-- コリジョン生成用:ブラー
public var m_Layer_ForTerrain_Blur:Sprite;
//-- コリジョン生成用:マスク
public var m_Layer_ForTerrain_Mask:Sprite;
//-- プレイヤー表示切り替え用:マスク
public var m_ShapeMask_ForPlayer:Shape;
//#Graphic
//- コリジョン
public var m_Bitmap_Terrain:Bitmap;//プレイヤーの移動を考慮した太いラインで描かれたコリジョン
public var m_Bitmap_Terrain_Base:Bitmap;//上のやつを作るための細いラインのコリジョン
//- マップ
public var m_Bitmap_Map:Bitmap;//ブロックの表示&下のブラーで不要な部分を隠すためのもの
public var m_Bitmap_Map_ForBlur:Bitmap;//コリジョンを作るためにブラーをかける画像
public var m_Bitmap_Map_ForGate_X:Bitmap;//通過可能領域を表示するもの:横に光を飛ばす
public var m_Bitmap_Map_ForGate_Y:Bitmap;//通過可能領域を表示するもの:縦に光を飛ばす
//#Player
public var m_Player:Player;
//==Func==
//Static Global Access
static private var m_Instance:GameMain;
static public function Instance():GameMain{return m_Instance;}
//#Init
public function GameMain(){
//Init Later (for Using "stage" etc.)
addEventListener(Event.ADDED_TO_STAGE, Init );
m_Instance = this;
}
public function Init(e:Event = null):void{
var x:int, y:int;
W = MAP[0].length * PANEL_LEN;
H = MAP.length * PANEL_LEN;
//Init Once Only
{
removeEventListener(Event.ADDED_TO_STAGE, Init );
}
//BG
{
addChild(new Bitmap(new BitmapData(W, H, false, 0xFFFFFF)));
//addChild(new Bitmap(new BitmapData(W, H, false, 0x000000)));//確認用
}
//Bitmap : Terrain
{
//Create : Main
{
m_Bitmap_Terrain = new Bitmap(new BitmapData(W, H, true, 0x00000000));
m_Bitmap_Terrain_Base = new Bitmap(new BitmapData(W, H, true, 0x00000000));
m_Bitmap_Terrain_Base.filters = [new BlurFilter(PANEL_LEN/2,PANEL_LEN/2)];
}
}
//Layer
{
//Create : Root
{
m_Layer_Root = new Sprite();
addChild(m_Layer_Root);
}
//Create : Terrain
{
m_Layer_Terrain = new Sprite();
m_Layer_Root.addChild(m_Layer_Terrain);
}
//Create : Blur
{
m_Layer_ForTerrain_Blur = new Sprite();
m_Layer_Terrain.addChild(m_Layer_ForTerrain_Blur);
//ブラー
m_Layer_ForTerrain_Blur.filters = [new BlurFilter(2,2)];//コリジョン用の線を作るため、本体より一回り大きく表示
}
//Create : Mask
{
m_Layer_ForTerrain_Mask = new Sprite();
m_Layer_Terrain.addChild(m_Layer_ForTerrain_Mask);
}
}
//Bitmap : Map
var PlayerX:int = 0;
var PlayerY:int = 0;
var GoalX:int = 0;
var GoalY:int = 0;
{//MAPの中身を解釈して、ブロック画像やプレイヤー位置を処理
//Create
{
//X
{
m_Bitmap_Map_ForGate_X = new Bitmap(new BitmapData(W, H, true, 0x00000000));
m_Layer_ForTerrain_Blur.addChild(m_Bitmap_Map_ForGate_X);
m_Bitmap_Map_ForGate_X.filters = [new GlowFilter(0x0088FF, 1.0, PANEL_LEN,1)];
}
//Y
{
m_Bitmap_Map_ForGate_Y = new Bitmap(new BitmapData(W, H, true, 0x00000000));
m_Layer_ForTerrain_Blur.addChild(m_Bitmap_Map_ForGate_Y);
m_Bitmap_Map_ForGate_Y.filters = [new GlowFilter(0x0088FF, 1.0, 1,PANEL_LEN)];
}
}
{
m_Bitmap_Map_ForBlur = new Bitmap(new BitmapData(W, H, true, 0x00000000));
m_Layer_ForTerrain_Blur.addChild(m_Bitmap_Map_ForBlur);
}
{
m_Bitmap_Map = new Bitmap(new BitmapData(W, H, true, 0x00000000));
m_Layer_ForTerrain_Mask.addChild(m_Bitmap_Map);
}
{
m_ShapeMask_ForPlayer = new Shape();
m_Layer_Terrain.addChild(m_ShapeMask_ForPlayer);
}
//Draw
{
var NumX:int = MAP[0].length;
var NumY:int = MAP.length;
var rect:Rectangle = new Rectangle(0,0, PANEL_LEN,PANEL_LEN);
var g:Graphics = m_ShapeMask_ForPlayer.graphics;
g.lineStyle(0, 0x000000, 0.0);
g.beginFill(0x000000, 1.0);
for(y = 0; y < NumY; y++){
rect.y = y * PANEL_LEN;
for(x = 0; x < NumX; x++){
rect.x = x * PANEL_LEN;
switch(MAP[y][x]){
case 0://空間
break;
case 1://ブロック
m_Bitmap_Map.bitmapData.fillRect(rect, 0xFF000000);
m_Bitmap_Map_ForBlur.bitmapData.fillRect(rect, 0xFFFFFFFF);
g.drawRect(rect.x, rect.y, rect.width, rect.height);
break;
case 2://隣接する空間と移動可能なブロック
m_Bitmap_Map.bitmapData.fillRect(rect, 0xFF000000);
m_Bitmap_Map_ForGate_X.bitmapData.fillRect(rect, 0xFF000000);
m_Bitmap_Map_ForGate_Y.bitmapData.fillRect(rect, 0xFF000000);
g.drawRect(rect.x, rect.y, rect.width, rect.height);
break;
case P://プレイヤー位置(それ以外は空間扱い)
PlayerX = rect.x + PANEL_LEN/2;
PlayerY = rect.y + PANEL_LEN/2;
break
case G://ゴール位置(それ以外は空間扱い)
GoalX = rect.x + PANEL_LEN/2;
GoalY = rect.y + PANEL_LEN/2;
break
}
}
}
g.endFill();
}
}
//Register Player
{
m_Player = new Player(PlayerX, PlayerY);
m_Layer_Root.addChild(m_Player);
}
//Register Goal
{
m_Layer_Root.addChild(new Goal(GoalX, GoalY));
}
//Call "Update"
{
addEventListener(Event.ENTER_FRAME, Update);
}
}
//#Update
static public var m_IsDirty:Boolean = true;
public function Update(e:Event=null):void{
const DeltaTime:Number = 1/24.0;
//Redraw Collision
if(m_IsDirty)
{//今のところ一回更新すればOKなのでこれで。
m_Bitmap_Terrain.bitmapData.fillRect(m_Bitmap_Terrain.bitmapData.rect, 0x00000000);//まずはリセットして
m_Bitmap_Terrain_Base.bitmapData.draw(m_Layer_Terrain);//細い線を描いて
m_Bitmap_Terrain.bitmapData.draw(m_Bitmap_Terrain_Base);//それを太くする
m_IsDirty = false;
}
//Camera
{//プレイヤーが画面に収まるようにRootの位置を調整
var CAMERA_W:int = stage.stageWidth;
var CAMERA_H:int = stage.stageHeight;
var trgX:Number = m_Player.x - CAMERA_W/2.0;
var trgY:Number = m_Player.y - CAMERA_H/2.0;
var MinX:Number = 0.0;
var MaxX:Number = W - CAMERA_W;
var MinY:Number = 0.0;
var MaxY:Number = H - CAMERA_H;
if(trgX > MaxX){
trgX = MaxX;
}
if(trgY > MaxY){
trgY = MaxY;
}
if(trgX < MinX){
trgX = MinX;
}
if(trgY < MinY){
trgY = MinY;
}
m_Layer_Root.x = -trgX;
m_Layer_Root.y = -trgY;
}
}
//#Move
public function IsCollision(i_X:int, i_Y:int):Boolean{
//範囲外は空間とみなす
{
if(i_X < 0){return false;}
if(i_X >= W){return false;}
if(i_Y < 0){return false;}
if(i_Y >= H){return false;}
}
//一定以上白ければ壁とみなす
var color:uint = m_Bitmap_Terrain.bitmapData.getPixel32(i_X, i_Y);
var r:uint = (color >> 16) & 0xFF;
return r > 0x02;//ここらへんはもうちょっと後で考え直したい
}
}
internal class Player extends Sprite{
//==Cost==
//#Input
static public var button_enum:int = 0;
static public const BUTTON_L:int = button_enum++;
static public const BUTTON_R:int = button_enum++;
static public const BUTTON_U:int = button_enum++;
static public const BUTTON_NUM:int = button_enum;
//#Move
static public const VEL_X:Number = GameMain.PANEL_LEN * 70.0/15;
static public const VEL_Y:Number = GameMain.PANEL_LEN * 200.0/16;
static public const ACC_Y:Number = GameMain.PANEL_LEN * 600.0/16;
//==Var==
//#StartPos
public var m_StartPosX:int = 0;
public var m_StartPosY:int = 0;
//#Input
public var m_Input:Array = new Array(BUTTON_NUM);
//#Move
public var m_VY:Number = 0.0;
public var m_OnGround:Boolean = false;
//==Function==
//#Init
public function Player(in_X:int, in_Y:int){
//Pos
{
this.x = m_StartPosX = in_X;
this.y = m_StartPosY = in_Y;
}
//Init Later (for Using "stage" etc.)
addEventListener(Event.ADDED_TO_STAGE, Init );
}
public function Init(e:Event = null):void{
var i:int;
//Init Once Only
{
removeEventListener(Event.ADDED_TO_STAGE, Init );
}
//Create Graphic
{
const Rad:int = GameMain.PANEL_LEN/4;
var shape:Shape;
var g:Graphics;
//黒
{
shape = new Shape();
g = shape.graphics;
g.lineStyle(0, 0x000000, 0.0);
g.beginFill(0x222222, 1.0);
g.drawCircle(0, 0, Rad);
g.endFill();
addChild(shape);
}
//白
{
shape = new Shape();
g = shape.graphics;
g.lineStyle(0, 0x000000, 0.0);
g.beginFill(0xDDDDDD, 1.0);
g.drawCircle(0, 0, Rad);
g.endFill();
addChild(shape);
shape.mask = GameMain.Instance().m_ShapeMask_ForPlayer;//地形が黒いところでしか白プレイヤーは表示されない
}
}
//Input
{
//Init
for(i = 0; i < BUTTON_NUM; i++){
m_Input[i] = false;
}
//Listener
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
}
//Call Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
}
//#Input
static public const KEY_R:int = 82;
public function KeyDown(e:KeyboardEvent):void{
switch(e.keyCode){
case Keyboard.LEFT: m_Input[BUTTON_L] = true; break;
case Keyboard.RIGHT: m_Input[BUTTON_R] = true; break;
case Keyboard.UP: m_Input[BUTTON_U] = true; break;
case KEY_R://位置のリセット
this.x = m_StartPosX;
this.y = m_StartPosY;
break;
}
}
public function KeyUp(e:KeyboardEvent):void{
switch(e.keyCode){
case Keyboard.LEFT: m_Input[BUTTON_L] = false; break;
case Keyboard.RIGHT: m_Input[BUTTON_R] = false; break;
case Keyboard.UP: m_Input[BUTTON_U] = false; break;
}
}
//#Update
public function Update(e:Event):void{
var DeltaTime:Number = 1.0 / 24.0;
//範囲外なら移動を諦める
{
const Range:int = GameMain.PANEL_LEN*2;
if(this.x < -Range){return;}
if(this.x >= GameMain.W + Range){return;}
if(this.y < -Range){return;}
if(this.y >= GameMain.H + Range){return;}
}
//Move
{
//Calc MoveVal
var MoveX:int;
var MoveY:int;
{
//X
{
MoveX = 0;
if(m_Input[BUTTON_L]){MoveX -= VEL_X * DeltaTime;}
if(m_Input[BUTTON_R]){MoveX += VEL_X * DeltaTime;}
}
//Y
{
//ジャンプ
if(m_OnGround && m_Input[BUTTON_U]){
m_VY = -VEL_Y;
m_OnGround = false;
}
//重力
m_VY += ACC_Y * DeltaTime;
MoveY = m_VY * DeltaTime;
}
}
//移動アルゴリズム実行
const BaseOffset:int = 1;//浮く量
var Offset:int;
var TrgX:int;
var TrgY:int;
var i:int;
//Move : Up
{
TrgX = this.x;
Offset = (MoveY < 0)? BaseOffset-MoveY: BaseOffset;
for(i = 0; i < Offset; i++){
TrgY = this.y - 1;
if(GameMain.Instance().IsCollision(TrgX, TrgY)){
//天井にぶつかったので速度は0にする
m_VY = 0;
break;
}
this.y = TrgY;
}
}
//Move : Horz
{
TrgX = this.x;
TrgY = this.y;
for(i = 0; i != MoveX; ){
if(i < MoveX){
i++;
TrgX++;
}else{
i--;
TrgX--;
}
if(GameMain.Instance().IsCollision(TrgX, TrgY)){break;}
this.x = TrgX;
}
}
//Move : Down
{
TrgX = this.x;
Offset = (MoveY > 0)? BaseOffset+MoveY: BaseOffset;
for(i = 0; i < Offset; i++){
TrgY = this.y + 1;
if(GameMain.Instance().IsCollision(TrgX, TrgY)){
//接地したとみなす
m_OnGround = true;//ジャンプできるようにフラグオン
m_VY = 0;//速度リセット
break;
}
this.y = TrgY;
}
}
}
}
}
internal class Goal extends Sprite{
public function Goal(in_X:int, in_Y:int){
//Pos
{
this.x = in_X;
this.y = in_Y;
}
//Graphic
{
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(2, 0xFFFF00, 1.0);
g.drawCircle(0, 0, GameMain.PANEL_LEN/2);
shape.filters = [new GlowFilter(0xFF0000, 1.0)];
addChild(shape);
}
//Call "Update"
{
addEventListener(Event.ENTER_FRAME, Update);
}
}
//#Update
public function Update(e:Event=null):void{
var GapX:int = GameMain.Instance().m_Player.x - this.x;
var GapY:int = GameMain.Instance().m_Player.y - this.y;
if(Math.sqrt(GapX*GapX+GapY*GapY) < GameMain.PANEL_LEN){
//プレイヤーがゴールに接触した
//じゃあ、なんかゴールを表示しとこう
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(8, 0xFFFF00, 1.0);
g.drawCircle(0,0, 100);
shape.filters = [new GlowFilter(0xFF0000, 1.0)];
addChild(shape);
//こいつの処理も外す
removeEventListener(Event.ENTER_FRAME, Update);
//本体も外しとこう
GameMain.Instance().removeEventListener(Event.ENTER_FRAME, GameMain.Instance().Update);
//プレイヤーも(ry
GameMain.Instance().m_Player.removeEventListener(Event.ENTER_FRAME, GameMain.Instance().m_Player.Update);
}
}
}