実験中2:「削りSTG」用のダメージ&回復表現
前回のやつにコアを使ってみるテスト。
ラインをショットで分断すれば、その先は不活性になる。
赤い部分は直接表示せずにマスクにした方が良さげ。
ひとまず実験はここまで。
/**
* 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/A9Xu
*/
// forked from o_healer's 実験中:「削りSTG」用のダメージ&回復表現
/*
実験中2:「削りSTG」用のダメージ&回復表現
概要
・前回の続き
・ライン寸断によるコアの不活性処理を確認したところまで
・バリアは面倒だったので試作も保留
・バリア:コアが活性中はコアはバリアで守られる
・ラインを寸断することでバリアを解除→コアに攻撃という流れを想定
操作方法
・十字キー
・自機の移動
・A:ラインで削るショット
・奥まで削るが回復が早い
・S:円で削るショット
・削りは浅いが範囲が広く回復が遅い
・マウス
・マウスの動いた部分を削る
ダメージ表現と回復処理の概要
・ダメージ部分をBitmapとして保持し、
「その部分を削る」「Glowをかけたものを重ねる」ことでダメージ、
「ブラー」「閾値で切り捨て相当処理」で回復させている
弾とOBJのヒットチェック解説
・BitmapDataにIndexを描き込むことで、「その位置にどのOBJが居るか」を判定する
・描き込むIndexは「Layer_Objectの何番目の子供か」に相当
・getChildAtで直接アクセスできる
・描き込む際はColorTransformでRGBがIndexになるようにして、それぞれのOBJを普通に描画
・ここのコストが高いので、実用的かどうかは怪しいところ
再設計用のメモ
・ダメージ表現は「削る部分」ではなく「残ってる部分」でやってみる
・コアをやられたら回復方向を逆にすることで自壊を表現できる?
・閾値を調整して、「ブラーによる減衰」ではなく「ブラーによる拡大」にする
・「Glow」をかけずに「PaletteMap」でやるパターンを試す(Glowだと負荷が高そう)
・コア&ラインの部分は「マスク」として使う
・そのまま表示するにはあまりにアレすぎる
・floodFillで透明を指定することで、「マスクあり=不活性」「マスクなし=活性」となるように
・CopyChannelでマスク相当になる?
・バリアは「接触時」だけでなく「接触後のマスキング」にも対応させる
・バリアの手前に弾が当たると、削りが長い場合に侵入を許してしまう
・なので、削られても即座にマスキングで復活させたい
・「残ってる部分」にバリアの部分を加算すればOK?
・マスキングだけだと「バリア以上にでかい削り」みたいなものでまたおかしくなる
・そこまででかいことはないと思うけど、少しはみ出るような状況はありうる
*/
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 = 0xC0;//この値以上なら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_Core: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_Core = 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{
Redraw();
Recover();
}
//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);
/*/
//コア&ライン画像をコピー
m_BitmapData_Glow.copyPixels(m_BitmapData_Core, BMD_RECT, POS_ZERO);
//Clear
m_BitmapData_Util_ARGB.fillRect(BMD_RECT, 0x00000000);
//食らった部分をDraw
m_BitmapData_Util_ARGB.draw(m_BitmapData_Damage);
//Effect
m_BitmapData_Util_ARGB.applyFilter(m_BitmapData_Util_ARGB, BMD_RECT, POS_ZERO, m_Filter_Effect);
//→GlowではなくPaletteMapで小さい値を発光に割り当てても良いかも
// ・0xFFなら透明、0x00も透明、0x01:薄い発光~0xFE:濃い発光でうまくいく?
//コア&ラインとエフェクトを合成
m_BitmapData_Glow.draw(m_BitmapData_Util_ARGB);
//中心のコアにつながっている部分を塗りつぶす
m_BitmapData_Glow.floodFill(W/2, 48, 0xFFFF0000);
//ブラー
m_BitmapData_Glow.applyFilter(m_BitmapData_Glow, BMD_RECT, POS_ZERO, m_Filter_Effect1);
//表示される部分でマスク
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);
}
}
}
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;
//移動量(1フレームあたり)
public var m_Move:int = 30;
//ダメージとして描画する形状
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){
var g:Graphics;
//Super Init
{
W = 8;
H = 16;
super.Init();
}
//Pos
{
this.x = in_X;
this.y = in_Y;
}
//Type
{
m_Type = in_Type;
}
//Damage Shape
{
m_DamageSprite.addChild(m_DamageShape);
g = m_DamageShape.graphics;
switch(m_Type){
case TYPE_LINE:
{
const LINE_W:int = 12;
const LINE_LEN:int = 32;
g.lineStyle(LINE_W, 0xFFFFFF, 1.0);
g.moveTo(0, LINE_W);
g.lineTo(0, -LINE_LEN);
}
break;
case TYPE_CIRCLE:
{
const RAD:int = 16;
g.lineStyle(0,0,0);
g.beginFill(0xFFFFFF, 1.0);
g.drawCircle(0,0, RAD);
g.endFill();
}
break;
}
}
/*
//m_Move
{//配列にした方が良いかも
switch(m_Type){
case TYPE_LINE:
m_Move = 20;
break;
case TYPE_CIRCLE:
m_Move = 5;
break;
}
}
//*/
//Graphic
{
var shape:Shape = new Shape();
g = shape.graphics;
g.lineStyle(0,0,0);
g.lineStyle(W/2, 0x888800, 1.0);
g.moveTo(W/2, W/2);
g.lineTo(W/2, H-W/2);
m_BitmapData_View.draw(shape);
var bmp:Bitmap = new Bitmap(m_BitmapData_View);
bmp.x = -W/2;
bmp.y = -W/2;
addChild(bmp);
}
}
//Update
override public function Update():void{
//消えるかチェック(何かあればtrueにして消す)
var KillFlag:Boolean = false;
//移動しつつ衝突判定
{
//現状では自分も相手も画像の中央を中心としていると考えているが、もう少し汎用性を高めた方が良いかも
for(var i:int = 0; i < m_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*3;
H = 128*1.5;
super.Init();
}
//元となる画像
{
//test
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
const Rad:int = 48;
g.lineStyle(0,0,0);
g.beginFill(0x222222, 1.0);
g.drawCircle(Rad, H-Rad, Rad);
g.drawCircle(W/2, Rad, Rad);
g.drawCircle(W-Rad, H-Rad, Rad);
g.endFill();
g.lineStyle(Rad*3/4, 0x222222, 1.0);
g.moveTo(Rad, H-Rad);
g.lineTo(W/2, Rad);
g.lineTo(W-Rad, H-Rad);
g.lineTo(Rad, H-Rad);//test
m_BitmapData_Ori.draw(shape);
//*
const d:int = 32;
const col:uint = 0xFF888888;
m_BitmapData_Core.fillRect(new Rectangle(Rad-d/2, H-Rad-d/2, d,d), col);
m_BitmapData_Core.fillRect(new Rectangle(W/2-d/2, Rad-d/2, d,d), col);
m_BitmapData_Core.fillRect(new Rectangle(W-Rad-d/2, H-Rad-d/2, d,d), col);
g.clear();
g.lineStyle(4, col, 1.0);
g.moveTo(Rad, H-Rad);
g.lineTo(W/2, Rad);
g.lineTo(W-Rad, H-Rad);
m_BitmapData_Core.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{
super.Update();
//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;
}
}
}