実験中:「削りSTG」用のダメージ&回復表現
概要
・「削りSTG」でメインとなるダメージ表現&自動回復処理の実験コード
・故に、色んな部分が仮コード
操作方法
・十字キー
・自機の移動
・A:ラインで削るショット
・奥まで削るが回復が早い
・S:円で削るショット
・削りは浅いが範囲が広く回復が遅い
・マウス
・マウスの動いた部分を削る
/**
* 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/vgDc
*/
/*
実験中:「削りSTG」用のダメージ&回復表現
概要
・「削りSTG」でメインとなるダメージ表現&自動回復処理の実験コード
・故に、色んな部分が仮コード
操作方法
・十字キー
・自機の移動
・A:ラインで削るショット
・奥まで削るが回復が早い
・S:円で削るショット
・削りは浅いが範囲が広く回復が遅い
・マウス
・マウスの動いた部分を削る
ダメージ表現と回復処理の概要
・ダメージ部分をBitmapとして保持し、
「その部分を削る」「Glowをかけたものを重ねる」ことでダメージ、
「ブラー」「閾値で切り捨て相当処理」で回復させている
弾とOBJのヒットチェック解説
・BitmapDataにIndexを描き込むことで、「その位置にどのOBJが居るか」を判定する
・描き込むIndexは「Layer_Objectの何番目の子供か」に相当
・getChildAtで直接アクセスできる
・描き込む際はColorTransformでRGBがIndexになるようにして、それぞれのOBJを普通に描画
・ここのコストが高いので、実用的かどうかは怪しいところ
*/
package {
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
[SWF(width="465", height="465", frameRate="30", backgroundColor="0xFFFFFF")]
public class GameMain extends Sprite {
//==Const==
//HitTest用
static public const OBJ_NUM_MAX:uint = 0xFD;//Objの登録可能最大数
static public const HIT_TEST_EMPTY_COLOR:uint = 0x0000FF;//Empty用の値
//==Var==
//Pseudo Singleton
static public var Instance:GameMain;
//レイヤー
public var m_Layer_Object:Sprite = new Sprite();
//HitTest画像
public var m_BitmapData_HitTest:BitmapData = new BitmapData(465, 465, false, HIT_TEST_EMPTY_COLOR);
//HitTest用ColorTransform
public var CT_SET_INDEX:Array;
//プレイヤー
public var m_Player:Player = new Player();
//ボス
public var m_Boss:Boss = new Boss();
//汎用
public var mtx:Matrix = new Matrix(1,0,0,1, 0,0);
//==Function==
//Init
public function GameMain():void {
var i:int;
//Pseudo Singleton
{
Instance = this;
}
//Layer
{
addChild(m_Layer_Object);
}
//Player
{
m_Player.x = 465/2;
m_Player.y = 465*3/4;
m_Layer_Object.addChild(m_Player);
}
//Boss
{
m_Boss.x = 465/2;
m_Boss.y = 465*1/4;
m_Layer_Object.addChild(m_Boss);
}
//CT_SET_INDEX
{
CT_SET_INDEX = new Array(OBJ_NUM_MAX);
for(i = 0; i < OBJ_NUM_MAX; i++){
CT_SET_INDEX[i] = new ColorTransform(0,0,0,1, 0,0,i,0xFF);
//一応、iを0xFFとかでマスクすることで制限を拡大できるが、普通は250体も居れば十分なはず
}
}
//Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
//Test
/*
{
//可視化チェック
addChild(new Bitmap(m_BitmapData_HitTest));
}
//*/
}
//Update
public function Update(e:Event=null):void{
// var DeltaTime:Number = 1.0 / stage.frameRate;
Update_Obj();
Update_HitTest();
}
//Update : Obj
public function Update_Obj():void{
var num:int = m_Layer_Object.numChildren;
for(var i:int = 0; i < num; i++){
//
var obj:GameObject = m_Layer_Object.getChildAt(i) as GameObject;
//Update
{
obj.Update();
}
//Check : Kill
{
if(obj.parent == null){//登録が解除された
num -= 1;//更新
i--;//相殺
continue;
}
}
}
}
//Update : HitTest
public function Update_HitTest():void{
//Clear
{
m_BitmapData_HitTest.fillRect(m_BitmapData_HitTest.rect, HIT_TEST_EMPTY_COLOR);
}
//Test
{
mtx.tx = m_Boss.x;
mtx.ty = m_Boss.y;
m_BitmapData_HitTest.draw(m_Boss, mtx, CT_SET_INDEX[1]);
}
}
}
}
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.ui.*;
class GameObject extends Sprite
{
//==Const==
//回復まわりのパラメータ
static public const RECOVER_BLUR_VAL:Number = 2.0;//ブラーの広がり方
static public const RECOVER_BLUR_THRESHOLD:uint = 0xA0;//この値以上なら0xFFへ、そうでなければ0x00へ増減させる(エッジ化)
static public const V:Number = 10;//減少させる値
static public const M:Number = (RECOVER_BLUR_THRESHOLD+V)/RECOVER_BLUR_THRESHOLD;
static public const CT_FOR_RECOVER:ColorTransform = new ColorTransform(M,M,M,M, -V,-V,-V,-V);
//エフェクトパラメータ
static public const CT_FOR_EFFECT:ColorTransform = new ColorTransform(1,1,1,1, 0,0,0,0xFF);
//static public const CT_FORCE_WHITE:ColorTransform = new ColorTransform(0,0,0,1, 0xFF,0xFF,0xFF,0);
//Utility
static public const POS_ZERO:Point = new Point(0,0);
public var BMD_RECT:Rectangle;
//==Var==
//サイズ指定
public var W:int = 32;
public var H:int = 32;
//元となる画像
public var m_BitmapData_Ori:BitmapData;
//実際の表示に使う画像
public var m_BitmapData_View:BitmapData;
//侵食表現用画像
public var m_BitmapData_Damage:BitmapData;
//特殊表現用画像
public var m_BitmapData_Glow:BitmapData;
//内部処理用
public var m_BitmapData_Util_ARGB:BitmapData;
public var m_BitmapData_Util_RGB:BitmapData;
//特殊表現用フィルター
public var m_Filter_Effect:GlowFilter = new GlowFilter(0x0088FF);
public var m_Filter_Effect1:BlurFilter = new BlurFilter(3,3);
//回復処理用フィルター
public var m_Filter_Recover:BlurFilter = new BlurFilter(RECOVER_BLUR_VAL, RECOVER_BLUR_VAL);
//==Function==
public function Init():void{
//画像
{
m_BitmapData_Ori = new BitmapData(W, H, true, 0x00000000);
//実際の表示に使う画像
m_BitmapData_View = new BitmapData(W, H, true, 0x00000000);
//侵食表現用画像
m_BitmapData_Damage = new BitmapData(W, H, true, 0x00000000);
//特殊表現用画像
m_BitmapData_Glow = new BitmapData(W, H, true, 0x00000000);
//内部処理用
m_BitmapData_Util_ARGB = new BitmapData(W, H, true, 0x00000000);
m_BitmapData_Util_RGB = new BitmapData(W, H, false, 0x000000);
}
//Utility
{
BMD_RECT = new Rectangle(0,0,W,H);
}
}
public function Update():void{}
}
class Player extends GameObject
{
//==Const==
// static public const W:int = 32;
//==Var==
//画像
// public var m_BitmapData_View:BitmapData = new BitmapData(W, W, true, 0x00000000);
//入力
public var m_InputL:Boolean = false;
public var m_InputR:Boolean = false;
public var m_InputU:Boolean = false;
public var m_InputD:Boolean = false;
public var m_InputShot_Line:Boolean = false;
public var m_InputShot_Circle:Boolean = false;
//==Function==
//Init
public function Player(){
//Super Init
{
W = 32;
H = 32;
super.Init();
}
//Graphic
{
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(0,0,0);
g.beginFill(0x880000, 1.0);
g.drawCircle(W/2, W/2, W/2);
g.endFill();
m_BitmapData_View.draw(shape);
var bmp:Bitmap = new Bitmap(m_BitmapData_View);
bmp.x = -W/2;
bmp.y = -W/2;
addChild(bmp);
}
//Input
{
addEventListener(
Event.ADDED_TO_STAGE,//ステージに追加されたら
function(e:Event):void{
//キー入力を見る
stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp);
}
);
}
}
//Update : Input
static public const KEY_A:int = 65;
static public const KEY_S:int = 83;
private function OnKeyDown(event:KeyboardEvent):void{
switch(event.keyCode){
case Keyboard.LEFT: m_InputL = true; break;
case Keyboard.RIGHT:m_InputR = true; break;
case Keyboard.UP: m_InputU = true; break;
case Keyboard.DOWN: m_InputD = true; break;
case Keyboard.SPACE:m_InputShot_Line = true; break;
case KEY_A: m_InputShot_Line = true; break;
case KEY_S: m_InputShot_Circle = true; break;
}
}
private function OnKeyUp(event:KeyboardEvent):void{
switch(event.keyCode){
case Keyboard.LEFT: m_InputL = false; break;
case Keyboard.RIGHT:m_InputR = false; break;
case Keyboard.UP: m_InputU = false; break;
case Keyboard.DOWN: m_InputD = false; break;
case Keyboard.SPACE:m_InputShot_Line = false; break;
case KEY_A: m_InputShot_Line = false; break;
case KEY_S: m_InputShot_Circle = false; break;
}
}
//Update
override public function Update():void{
//単純移動
const Move:int = 5;
if(m_InputL){this.x -= Move;}
if(m_InputR){this.x += Move;}
if(m_InputU){this.y -= Move;}
if(m_InputD){this.y += Move;}
//ショット
if(m_InputShot_Line){
parent.addChild(new Bullet(this.x, this.y, Bullet.TYPE_LINE));
m_InputShot_Line = false;//強制的に単発にしてみる
}
if(m_InputShot_Circle){
parent.addChild(new Bullet(this.x, this.y, Bullet.TYPE_CIRCLE));
m_InputShot_Circle = false;//強制的に単発にしてみる
}
}
}
class Bullet extends GameObject
{
//==Const==
// static public const W:int = 8;
static public var TypeIter:int = 0;
static public const TYPE_LINE:int = TypeIter++;
static public const TYPE_CIRCLE:int = TypeIter++;
//==Var==
//画像
// public var m_BitmapData_View:BitmapData = new BitmapData(W, W, true, 0x00000000);
//Type
public var m_Type:int = 0;
//ダメージとして描画する形状
public var m_DamageShape:Shape = new Shape();
public var m_DamageSprite:Sprite = new Sprite();
//==Function==
//Init
public function Bullet(in_X:int, in_Y:int, in_Type:int){
//Super Init
{
W = 8;
H = 8;
super.Init();
}
//Pos
{
this.x = in_X;
this.y = in_Y;
}
//Type
{
m_Type = in_Type;
}
//Graphic
{
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(0,0,0);
g.beginFill(0x888800, 1.0);
g.drawCircle(W/2, W/2, W/2);
g.endFill();
m_BitmapData_View.draw(shape);
var bmp:Bitmap = new Bitmap(m_BitmapData_View);
bmp.x = -W/2;
bmp.y = -W/2;
addChild(bmp);
}
//Damage Shape
{
m_DamageSprite.addChild(m_DamageShape);
g = m_DamageShape.graphics;
switch(m_Type){
case TYPE_LINE:
{
const LINE_W:int = 8;
const LINE_LEN:int = 64;
g.lineStyle(LINE_W, 0xFFFFFF, 1.0);
g.moveTo(0, 0);
g.lineTo(0, -LINE_LEN);
}
break;
case TYPE_CIRCLE:
{
const RAD:int = 12;
g.lineStyle(0,0,0);
g.beginFill(0xFFFFFF, 1.0);
g.drawCircle(0,0, RAD);
g.endFill();
}
break;
}
}
}
//Update
override public function Update():void{
const Move:int = 10;
//消えるかチェック(何かあればtrueにして消す)
var KillFlag:Boolean = false;
//移動しつつ衝突判定
{
//現状では自分も相手も画像の中央を中心としていると考えているが、もう少し汎用性を高めた方が良いかも
for(var i:int = 0; i < Move; i++){
this.y -= 1;
var index_hit:uint = GameMain.Instance.m_BitmapData_HitTest.getPixel(this.x, this.y);
if(index_hit != GameMain.HIT_TEST_EMPTY_COLOR){
//ヒットした相手
var obj_hit:GameObject = GameMain.Instance.m_Layer_Object.getChildAt(index_hit) as GameObject;
//形状
m_DamageShape.x = this.x - obj_hit.x + obj_hit.W/2;
m_DamageShape.y = this.y - obj_hit.y + obj_hit.H/2;
//相手のダメージにDraw
obj_hit.m_BitmapData_Damage.draw(m_DamageSprite, null, null, BlendMode.ADD);
//自分は消える
KillFlag = true;
break;
}
}
}
//範囲チェック
{
if(this.x < 0){KillFlag = true;}
if(this.x > 465){KillFlag = true;}
if(this.y < 0){KillFlag = true;}
if(this.y > 465){KillFlag = true;}
}
//死亡チェック
{
if(KillFlag){
parent.removeChild(this);
}
}
}
}
class Boss extends GameObject
{
//最終的に処理はプレイヤー側と統合して削り合いにしたい
//==Const==
// static public const W:int = 128;
// static public const H:int = 128;
//==Var==
//test
public var mouseX_Old:int = 0;
public var mouseY_Old:int = 0;
//==Function==
//Init
public function Boss(){
Init();
}
override public function Init():void{
//Super Init
{
W = 128;
H = 128;
super.Init();
}
//元となる画像
{
//test
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.beginFill(0x222222, 1.0);
g.drawCircle(W/2, H/2, W/2);
g.endFill();
m_BitmapData_Ori.draw(shape);
}
/*
//test:ダメージ画像
{
//test
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.beginFill(0xFFFFFF, 1.0);
g.drawCircle(0, H/2, W/2);
g.endFill();
m_BitmapData_Damage.draw(shape);
}
//*/
//表示画像の登録
{
var bmp_view:Bitmap = new Bitmap(m_BitmapData_View);
bmp_view.x = -W/2;
bmp_view.y = -H/2;
addChild(bmp_view);
}
}
//Update
override public function Update():void{
Redraw();
Recover();
//test
{
if(mouseX_Old != 0 && mouseY_Old != 0){
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(8, 0xFFFFFF, 1.0);
g.moveTo(mouseX_Old+W/2, mouseY_Old+H/2);
g.lineTo(mouseX+W/2, mouseY+H/2);
m_BitmapData_Damage.draw(shape);
}
mouseX_Old = mouseX;
mouseY_Old = mouseY;
}
}
//Redraw
public function Redraw():void{
//まずは元となる画像でクリア
{
m_BitmapData_View.copyPixels(m_BitmapData_Ori, BMD_RECT, POS_ZERO);
}
//攻撃された部分は消す(αを0にする)
{
/*
//上手くいかない
//Ori:A => Util:R
m_BitmapData_Util_ARGB.copyChannel(m_BitmapData_View, BMD_RECT, POS_ZERO, BitmapDataChannel.ALPHA, BitmapDataChannel.RED);
//Util:R -= Damage:R
m_BitmapData_Util_ARGB.draw(m_BitmapData_Damage, null, null, BlendMode.SUBTRACT);
//Util:R => Ori:A
m_BitmapData_View.copyChannel(m_BitmapData_Util_ARGB, BMD_RECT, POS_ZERO, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
//*/
//*
//上手くいく
//Ori:A => Util:R
m_BitmapData_Util_RGB.copyChannel(m_BitmapData_View, BMD_RECT, POS_ZERO, BitmapDataChannel.ALPHA, BitmapDataChannel.RED);
//Util:R -= Damage:R
m_BitmapData_Util_RGB.draw(m_BitmapData_Damage, null, null, BlendMode.SUBTRACT);
//Util:R => Ori:A
m_BitmapData_View.copyChannel(m_BitmapData_Util_RGB, BMD_RECT, POS_ZERO, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
//*/
/*
//上手くいく
//Ori:A => Util:RGB
m_BitmapData_Util_RGB.draw(m_BitmapData_View, null, CT_FORCE_WHITE);
//Util:RGB -= Damage:RGB
m_BitmapData_Util_RGB.draw(m_BitmapData_Damage, null, null, BlendMode.SUBTRACT);
//Util:R => Ori:A
m_BitmapData_View.copyChannel(m_BitmapData_Util_RGB, BMD_RECT, POS_ZERO, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
//*/
}
//特殊表現を追加
{
//Clear
m_BitmapData_Glow.fillRect(BMD_RECT, 0x00000000);
//Draw
m_BitmapData_Glow.copyPixels(m_BitmapData_Damage, BMD_RECT, POS_ZERO);
//Effect
m_BitmapData_Glow.applyFilter(m_BitmapData_Glow, BMD_RECT, POS_ZERO, m_Filter_Effect);
//→GlowではなくPaletteMapで小さい値を発光に割り当てても良いかも
// ・0xFFなら透明、0x00も透明、0x01:薄い発光~0xFE:濃い発光でうまくいく?
//表示される部分でマスク
m_BitmapData_Glow.copyChannel(m_BitmapData_View, BMD_RECT, POS_ZERO, BitmapDataChannel.ALPHA, BitmapDataChannel.ALPHA);
m_BitmapData_Glow.colorTransform(BMD_RECT, CT_FOR_EFFECT);
//結果を合成
//m_BitmapData_View.draw(m_BitmapData_Glow);
m_BitmapData_View.draw(m_BitmapData_Glow, null, null, BlendMode.ADD);
}
}
//Recover
public function Recover():void{
//まずはブラーでエッジ部分を弱める
{
m_BitmapData_Damage.applyFilter(m_BitmapData_Damage, BMD_RECT, POS_ZERO, m_Filter_Recover);
}
//ColorTransformで減衰調整
{
m_BitmapData_Damage.colorTransform(BMD_RECT, CT_FOR_RECOVER);
}
}
}