Adobe AIR + Starling +ベクターグラフィックスのラスタライズ





Adobe AIRでiOSとAndroid向けのゲームを作り始めてからしばらく経ちました。 今日、さまざまな画面解像度のゲームを作成する方法を共有したいと考えています。このアプローチをプロジェクトでうまく使用しています。



ご存知のように、さまざまな画面解像度に合わせてゲームグラフィックスを準備する方法はいくつかあります。



グラフィックで複数のパックを使用する



ゲームグラフィックスの形成のための最も一般的なアプローチ。 各解像度が独自の方法でスケジュールを解決できるようにします。 たとえば、小さな画面では、さまざまな要素の精緻化と詳細化が最小限に抑えられ、一部の詳細は完全に省略されます。 しかし、そのようなセットはかなりの重量があり、すべての解像度ではありません。テクスチャスケールの後、見た目はきれいです。 残忍な画面解像度のRetinaディスプレイが登場した後、開発者は既存の3つのテクスチャパックにもう1つ追加する必要がありました。



ピクセルアートを描く



ゲーム内の小さなテクスチャを備えたアトラスのパックを、1つの画面解像度のみで使用できます。これは、任意のサイズに拡大縮小できます。 正方形は正方形です。 少なくともsd、少なくともxxxhdでは、ピクセルアートはピクセルアートのように見えます。 さらに、ピクセルアートは比較的簡単に描画できます。



ベクターグラフィックス



現在の画面解像度でゲーム内の1パックのアトラスを使用でき、重さはほとんどなく、解像度を損なうことなく解像度に到達し、非常に見やすく、非常に簡単に描画できます。 これはまさに私が欲しかったものです。



しかし、それほど単純ではありません。 実際、すべてのベクターグラフィックスはCPUで処理されます。つまり、携帯電話でそのようなグラフィックスを使用すると、ブレーキがかかってしまいます。 私のゲームCity 2048の最初のバージョンはまさにそれでしたが、驚くべきことにそれはそれ自体でかなりうまく機能しましたが、25-40 fpsを生成しました。 ゲームのテストバージョンを起動することで、携帯電話が手にぶら下がって溶けてしまうと思っていましたが、できませんでした。 私の他のDots Tailsゲームもベクターグラフィックスを使用して動作していると言うことができます。その理由があります。



生産性を向上させるには、GPUですべてのゲームグラフィックスを描画する必要があります。このため、Stage3DとStarlingを使用します。 別々のベクター要素から、アプリケーションの実行中に一度に適切なサイズのラスタースプリットを作成する必要があることがわかりました。 これを実装する方法について説明します。



使用する前に、ベクターグラフィックスを目的のサイズに拡大し、アトラスにレイアウトして焼き付ける必要があります。 これらの目的のために、 エミリアーノ・アンジェリーニのわずかに修正されたクラス「ダイナミックテクスチャアトラスとビットマップフォントジェネレーター」を使用し 、アニメーションなしの単純なテクスチャアトラスのみを作成しました。



動作原理は次のとおりです。



1.ゲームのアートをAdobe Flash Pro(または他のベクターエディターで描画し、Flash Proに転送します)







2.グラフィック要素を含むスプライトを作成し、ASで使用できるようにします。 彼から私たちが精神を動かすのは彼からです。







3.希望のスケジュールをこのスプライトにプッシュします。 512x512のサイズに収まるように要素を配置しようとしました。 スケーリングでは、アトラスのサイズが4kを超えてはならないため、これが必要です。 レイアウトの設計では、常に600x800のサイズを使用するため、描画および配置された要素は見栄えがよく、2kのサイズを超えてクロールされません。 したがって、グラフィック要素はテーマに従って配置する必要があります。たとえば、私のゲームでは、GUIを含むレイヤーはゲームグラフィックの上にあるため、GUIとゲーム要素を使用して2つの別個のアトラスを作成します異なるアトラスで。 これは、ドロキュラの数を減らすのに役立ちます。







4.アトラス内の各要素に名前を割り当てることを忘れないでください。







5. .swcをリソースとともにエクスポートし、プロジェクトに接続します。







6.ソフトウェア部分にアクセスします。 まず、リソースをプルするスケルトンを計算します。



//    ,   iPad2 var _stageWidth:Number = 768; var _stageHeight:Number = 1024; //  - var defaultScreenWidth:Number = 600; var defaultScreenHeight:Number = 800; //     ,     .     _scaleX = _stageWidth / defaultScreenWidth; _scaleY = _stageHeight / defaultScreenHeight; _minScale = Math.min(_scaleX, _scaleY);
      
      





7. TextureManager.asクラスをプロジェクトに追加し、SWCからアトラス名を書き込みます



TextureManagerクラスのコンテンツ
 package com.Extension { import avmplus.getQualifiedClassName; import com.Event.GameActiveEvent; import com.Module.EventBus; import com.greensock.TweenNano; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.Sprite; import flash.display.StageQuality; import flash.geom.Matrix; import flash.geom.Rectangle; import starling.display.Image; import starling.display.Sprite; import starling.textures.Texture; import starling.textures.TextureAtlas; public class TextureManager { //         ,   PivotPoint   SWC private static var textureAdditionalData:Object = {}; //     private static var textureAtlases:Vector.<TextureAtlas> = new <TextureAtlas>[]; //      // !!! (      SWC  ) private static var toParse:Array = [ [guiAtlas, ScaleManager.minScale], [gameAtlas, ScaleManager.minScale] ]; //          public static function getSprite(textureName:String, smooth:String = "none"):starling.display.Sprite { if (textureAdditionalData.hasOwnProperty(textureName)) { var addition:Object = textureAdditionalData[textureName]; var image:Image = new Image(findTexture(textureName)); image.x = -addition["x"]; image.y = -addition["y"]; image.textureSmoothing = smooth; var result:starling.display.Sprite = new starling.display.Sprite(); result.addChild(image); return result; } throw new Error("[!!!] Texture '" + textureName + "' not found."); } //     public static function getTexture(textureName:String):Texture { return findTexture(textureName); } // ,      .   ,      . public static function createAtlases():void { if (!textureAtlases.length) { nextParseStep(); return; } throw new Error("[!!!] Texture atlases already."); } //    private static function nextParseStep():void { if (toParse.length) { var nextStep:Array = toParse.pop(); TweenNano.delayedCall(.15, TextureManager.createAtlas, nextStep); } else { //  ,      . EventBus.dispatcher.dispatchEvent(new GameActiveEvent(GameActiveEvent.GAME_START, true)); } } //      private static function findTexture(textureName:String):Texture { var result:Texture; for each (var atlas:TextureAtlas in textureAtlases) { result = atlas.getTexture(textureName); if (result) { return result; } } throw new Error("[!!!] Texture '" + textureName + "' not found."); } //      SWC    private static function createAtlas(swcPack:Class, scaleFactor:Number):void { var pack:flash.display.Sprite = (new swcPack()) as flash.display.Sprite; var itemsHolder:Array = []; var canvas:flash.display.Sprite = new flash.display.Sprite(); var children:uint = pack.numChildren; for (var i:uint = 0; i < children; i++) { var selected:DisplayObject = pack.getChildAt(i); var realX:Number = selected.x; var realY:Number = selected.y; selected.scaleX *= scaleFactor; selected.scaleY *= scaleFactor; var bounds:Rectangle = selected.getBounds(selected.parent); bounds.x = Math.floor(bounds.x - 1); bounds.y = Math.floor(bounds.y - 1); bounds.height = Math.round(bounds.height + 2); bounds.width = Math.round(bounds.width + 2); var drawRect:Rectangle = new Rectangle(0, 0, bounds.width, bounds.height); var bData:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0); var mat:Matrix = selected.transform.matrix; mat.translate(-bounds.x, -bounds.y); bData.drawWithQuality(selected, mat, null, null, drawRect, false, StageQuality.BEST); var pivotX:int = Math.round(realX - bounds.x); var pivotY:int = Math.round(realY - bounds.y); textureAdditionalData[selected.name] = {x:pivotX, y:pivotY}; var item:flash.display.Sprite = new flash.display.Sprite(); item.name = selected.name; item.addChild(new Bitmap(bData, "auto", false)); itemsHolder.push(item); canvas.addChild(item); } layoutChildren(); var canvasData:BitmapData = new BitmapData(canvas.width, canvas.height, true, 0x000000); canvasData.draw(canvas); var xml:XML = new XML(<TextureAtlas></TextureAtlas>); xml.@imagePath = (getQualifiedClassName(swcPack) + ".png"); var itemsLen:int = itemsHolder.length; for (var k:uint = 0; k < itemsLen; k++) { var itm:flash.display.Sprite = itemsHolder[k]; var subText:XML = new XML(<SubTexture />); subText.@name = itm.name; subText.@x = itm.x; subText.@y = itm.y; subText.@width = itm.width; subText.@height = itm.height; xml.appendChild(subText); } var texture:Texture = Texture.fromBitmapData(canvasData); var atlas:TextureAtlas = new TextureAtlas(texture, xml); textureAtlases.push(atlas); function layoutChildren():void { var xPos:Number = 0; var yPos:Number = 0; var maxY:Number = 0; var maxW:uint = 512 * ScaleManager.atlasSize; var len:int = itemsHolder.length; var itm:flash.display.Sprite; for (var i:uint = 0; i < len; i++) { itm = itemsHolder[i]; if ((xPos + itm.width) > maxW) { xPos = 0; yPos += maxY; maxY = 0; } if (itm.height + 1 > maxY) { maxY = itm.height + 1; } itm.x = xPos; itm.y = yPos; xPos += itm.width + 1; } } nextParseStep(); } public function TextureManager() { throw new Error("[!!!] Used private class."); } } }
      
      





createAtlasメソッドで何が起こるかについてもう少し:



7.1。 SWCのアトラス内の各要素はスケーリングされ、Pivo​​tPointの座標を保存し、ビットマップで描画してキャンバスコンテナーに追加します。



7.2。 キャンバスコンテナー内の要素を次々に配置して、目的のサイズのアトラスに収まるようにします



7.3。 BitmapDataおよび.XMLジェネレーターでキャンバスコンテナーを描画する



7.4。 結果のBitmapDataと.XMLから、スターリングTextureAtlasを作成します



" 7.5。 結果のアトラスはtextureAtlasesコンテナに追加されます。



8.ゲームの開始時に、スターリング用のアトラスを作成します



 TextureManager.createAtlases();
      
      





9.シーンに必要なスプライトを追加します



 var tileView:starling.display.Sprite = TextureManager.getSprite("rotateView"); this.addChild(tileView);
      
      





最後に何が得られますか? 重量がほとんどない美しいグラフィックは、品質を損なうことなく任意の大きな画面サイズに拡大します。 この場合、ゲームは安定した60fpsで実行されます。 私にとって個人的には、もう1つのプラスは、ベクターでは描画が非常に簡単なことです。ただし、私はアーティストではありませんが、ベクターで何かをすることができます。







City 2048、Quadtris、およびPlacid Placeゲームでベクターグラフィックスラスタライズを使用しています。 このアプローチの実際の動作に興味がある場合は、Apple App StoreとGoogle Playで見つけることができます。 残念ながら、アプリケーションへの直接リンクを残すことはできません。



実際、それがすべてです。 ご清聴ありがとうございました。



All Articles