まよねーず工場

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--/--/-- --:-- |スポンサー広告  

Unityを使ったパレット変換

現在仕事でUnityを使用しております。
画像の色違いを扱うときに色別に画像データを持つといろいろとメモリがやばくなるので
プログラム内で直接テクスチャの色をつけて生成することにしました。


GUI.Textureには
GetPixel
SetPixel
Apply
LoadImage
EncodeToPNG
といったメソッドが存在し
width
height
というプロパティが存在します。

GetPixel( x , y )でテクスチャ内のピクセルの座標(0,0なら一番左端)を指定し、
そこからカラーを持ってきます。
そしてそこのカラーに新しいカラーを入れて
SetPixel( x , y , color )にカラーのセット

最後にApply()メソッドを呼ぶことで色の変更は終了


といった感じになります。




///


/// 画像カラー情報クラス
/// どこの座標の色は何色か?という情報を持つ
///

class GraphicColor{
public int x;
public int y;
public Color color; //変換するカラー


public GraphicColor( int _x , int _y , Color _color )
{
set( _x , _y , _color );
}

public GraphicColor()
{
x = 0;
y = 0;
color = new Color();
}

public void set( int _x , int _y , Color _color )
{
x = _x;
y = _y;
color = _color;
}
}




///
/// 色彩変換を行う
///

///
public static Texture2D palletConvert( AlphaChannel alpha , Color blendColor , Texture2D texture )
{
Texture2D result = texture;
for( int i = 0 ; i < alpha.Length ; i++ ){
Color c = new Color();
c.r = alpha[i].color.r;
c.g = alpha[i].color.g;
c.b = alpha[i].color.b;
c.a = alpha[i].color.a;

c = colorBlending( blendColor.r , blendColor.g , blendColor.b , c );
result.SetPixel( alpha[i].x , alpha[i].y , c );
}

result.Apply();
return result;
}




今回はレイヤーマスクのようなものを先に読み込み、
白黒のグラデーションからパレット変換の範囲を指定するというプログラムを作る必要があったので
以下のようなクラスを作成




///
/// アルファチャンネルクラス
///

class AlphaChannel{
private Texture2D alphaChannel;
private List enableArea; //アルファ範囲

public GraphicColor this[int index]{
get{
return enableArea[index];
}
}

public int Length{
get{
return enableArea.Count;
}
}

public Texture2D getAlphaChannel{
get{
return alphaChannel;
}
}

///
/// アルファチャンネル画像を取り込む
///

///
public AlphaChannel( string fileName )
{
enableArea = new List();
alphaChannel = (Texture2D)Resources.Load( fileName );
ImportAlphaChannel();
}

///
/// チャンネル範囲を調べる
/// カラーの付いた部分はチャンネル範囲、
/// 真っ白、アルファ値ゼロは評価範囲には含まれない
///

public void ImportAlphaChannel()
{
for( int y = 0 ; y < alphaChannel.height ; y++ ){
for( int x = 0 ; x < alphaChannel.width ; x++ ){
Color pixelColor = alphaChannel.GetPixel( x , y );
Color c = new Color();
c.r = pixelColor.r;
c.g = pixelColor.g;
c.b = pixelColor.b;
c.a = pixelColor.a;

//アルファ値0とホワイトカラーはノーカウント
if( c.a == 0.0f ){
continue;
}
if( c.r == 1.0f && c.g == 1.0f && c.b == 1.0f ){
continue;
}

enableArea.Add( new GraphicColor( x , y , c ) );
}
}
}

public AlphaChannel()
{
alphaChannel = null;
enableArea = new List();
}
}




なるべく原色ベースに色変換するほうが綺麗に塗り変わるので
フォトショでいうところの色彩変換を行います。
///
/// 原色を維持しながら加算成分をブレンドします
/// 引数で渡された色 / 最大値から出された割合 * 加算成分で計算して色彩を変える
///

/// 加算成分r
/// 加算成分g
/// 加算成分b
/// 変更したいカラー
///
public static Color colorBlending( float r , float g , float b , Color color )
{
Color result = new Color();
result.r = color.r;
result.g = color.g;
result.b = color.b;
result.a = color.a;
result.r += ( color.r / 1.0f ) * r;
result.g += ( color.g / 1.0f ) * g;
result.b += ( color.b / 1.0f ) * b;

return result;
}




なお、GetPixelとSetPixelで変換したテクスチャは元に戻りません。
Project内にあるテクスチャはプログラムを終了しても塗り変わったままなので、

プログラム内で独自にリソースから持ってきたテクスチャをベースに新しいテクスチャを作成する必要があります。



///
/// テクスチャを生成する
/// Project内の画像を直接色彩変換すると元に戻らないので
/// 必ずこのクラスで生成したテクスチャを使うこと。
///

///
///
public static Texture2D BuildingTexture( Texture2D texture )
{
Texture2D result = new Texture2D( texture.width , texture.height );
byte[] texbyte = texture.EncodeToPNG();

result.LoadImage( texbyte );
return result;
}





こんな感じのプログラムにより、無事色彩変換に成功。
といった感じで今日はUnityユーザーにしかわからないネタでした




ではでは

スポンサーサイト

2012/05/11 19:27 |プログラムCOMMENT(0)TRACKBACK(0)  

アクションゲームの考え方~床との判定~

現在アクションゲームの勉強中、
数学やプログラムの参考書などを目に通しながら試行錯誤しているNOWです

というわけで忘れないよう、
自分なりに考え方をまとめてみることにします。


<今回は地面との衝突判定についてのお話し>


とりあえずこんな図を作ってみます

アクションプログラミング~線と円の接触判定~


やたらといろんな文字がでてきて頭がパーンしそうですが、
ひとつずつ追っていけば大丈夫かと思われます。



まず、物体というのは今回は仮にプレイヤーということにしていますのでplという名前がついています。
プレイヤーの座標は( pl.x , pl.y )ということになります。


そして次に線の座標
線というのはようするに床です。

2Dアクションではこの線と円の衝突を判定していきます。

線の座標は開始点のx,yと終了点のx,yの二つからなりますので
( line.start.x , line.start.y )と( line.end.x , line.end.y )
の二つが存在することになります。



この物体の座標と線の開始点からなる線は
「ベクトルa」としています。


つまりベクトルa(aVectという名前にしておきます)は
物体の座標 - 線開始点の座標

aVect.x = pl.x - line.start.x
aVect.x = pl.y - line.start.y

から成ります。


次に床のベクトルを求めます。


線自体のベクトルを「ベクトルb」とします


ベクトルb(bVect)は
線の終了点 - 開始点で求めます

つまり


bVect.x = line.end.x - line.tart.x
bVect.y = line.end.y - line.start.y


でbベクトルを求められます。




aベクトルとbベクトルを求めることで
このベクトル間の内積(Dot)が求められます



内積は
Dot = ( aVect.x * bVect.x ) + ( aVect.y * bVect.y )
で求められるようです




そして次に線の長さ( bVectLength )を求めます



bVectLengthの長さは

( bVect.x * bVect.x ) + ( bVect.y * bVect.y )
で2乗の長さを求めることが可能、
これにルートをかけることで
sqrt( ( bVect.x * bVect.x ) + ( bVect.y * bVect.y ) )とすることで

本来の長さを求めることができますが、
今回は2乗の長さを求めてみます。



内積がこのベクトルの長さ2乗以上長さ以下のポジション内にいる場合、
「有効範囲内にいる」という判定ができます


つまり
if( Dot > bVectLength && Dot < bVectLength )が成立すれば

「少なくとも物体は線の範囲内にいるよ!

ということになるのです。


当然これだけでは当たり判定は成立しません。

結果論的には図にも書いたのですが、
線と物体との距離(distance)を求められれば当たり判定成立はもうすぐだったりするようです。

そのためにはaベクトルの長さに合わせてbベクトルが変化する必要があるらしく、


そのために単位ベクトル(bVectUnit)というのを求めてみます。
いわゆる長さ1のベクトルです。

まず正規の長さを求めます
Length = sqrt( ( bVect.x * bVect.x ) + ( bVect.y * bVect.y ) )
そして
bVectUnit.x = Length / bVect.x
bVectUnit.y = Length / bVect.y

といった具合に長さからベクトル座標で割ることで単位ベクトルを求めることができます。


こうして求めた単位ベクトル(bVect2)とaベクトルの内積(Dot2)を求めます。

この内積と単位ベクトルを計算するとaベクトルの長さに合わせて延長されたbベクトルが作れます

bVect.x = Dot2 * bVectUnit.x
bVect.y = Dot2 * bVectUnit.y

そしてこのbVect2に線の開始点の座標を足しますと
物体と線を結ぶ交点(Intersection)を求められるのです。



あとは物体の座標からIntersectionの座標を引くことで
ベクトルdistanceが求められます。



distance.x * distance.x + distance.y * distance.y < pl.range * pl.range

この式が成立したら当たり判定が成立となります。




という感じで当たり判定が完成、ただしまだ斜めの坂の計算がこれでは出来ないので
その辺もまたまとめてみようと思っています。


ではでは

2011/04/09 20:10 |プログラムCOMMENT(0)TRACKBACK(0)  

 | BLOG TOP | 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。