2014-06-11

FlashでRPGのマップスクロールを作成

flash rpg map scroll FlashでRPGのマップスクロールを作成

4x4のマップではスクロールするまでもないので、16x16のマップで、DragonQuestふうにマップがスクロールするようにする。

Flashで外部swfに埋め込んだMovieClipを使う の続き

ところでマップはどう作ろうか?

とりあえずExcel(互換ソフト)で作ってみた。

しかし、Excelや互換ソフトの関数だとセルの色を認識せず、セルを色分けてもそれをCSV出力などできないらしい。

Tiled Map Editor を使い、マップを並べてみる。 http://gushernobindsme.hatenablog.com/entry/2014/03/01/155532 http://2dgames.jp/2013/08/01/tiledmapeditor/ を参考にした。なんだか難しそうに思えたが、[ファイル-新しいファイル]で開始して、[マップ-新しいマップセット]でマップタイル(マップチップ)画像を読み込むだけだった。

必要なマップタイル(マップチップ)はFlash Proで作成して、32x32単位で配置して、72dpiでpng出力して、TiledMapEditorで[マップ-新しいマップセット]で読み込んだ。

Tiled Map Editorで[ファイル - 名前をつけて保存]で出力したjson形式だと以下のようなファイルになりましたよ。

{ "height":16,
 "layers":[
        {
         "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 3, 17, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, 3, 3, 1, 1, 3, 3, 15, 15, 3, 3, 3, 3, 1, 3, 3, 15, 15, 3, 1, 1, 3, 15, 15, 17, 15, 3, 3, 3, 3, 3, 3, 3, 15, 5, 1, 1, 3, 15, 15, 5, 5, 15, 3, 3, 3, 3, 3, 15, 15, 5, 1, 1, 3, 3, 15, 5, 5, 15, 15, 3, 3, 3, 3, 15, 17, 5, 1, 1, 1, 3, 15, 5, 5, 5, 5, 3, 3, 3, 15, 15, 5, 5, 1, 1, 1, 1, 15, 5, 5, 5, 5, 3, 3, 3, 3, 15, 15, 15, 1, 1, 1, 3, 15, 15, 5, 5, 3, 3, 3, 3, 17, 3, 3, 15, 1, 1, 1, 3, 17, 15, 15, 3, 3, 1, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, 5, 3, 15, 15, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 3, 15, 1, 1, 15, 15, 1, 15, 15, 1, 1, 1, 3, 5, 5, 5, 3, 3, 1, 15, 5, 5, 5, 15, 17, 15, 1, 1, 1, 3, 3, 5, 5, 5, 5, 5, 5, 15, 5, 5, 5, 15, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
         "height":16,
         "name":"\u30bf\u30a4\u30eb\u30fb\u30ec\u30a4\u30e4\u30fc1",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":16,
         "x":0,
         "y":0
        }],
 "orientation":"orthogonal",
 "properties":
    {

    },
 "tileheight":32,
 "tilesets":[
        {
         "firstgid":1,
         "image":"maptipCS3.png",
         "imageheight":110,
         "imagewidth":224,
         "margin":0,
         "name":"maptipCS3",
         "properties":
            {

            },
         "spacing":0,
         "tileheight":32,
         "tilewidth":32
        }],
 "tilewidth":32,
 "version":1,
 "width":16
}

この数字をマップチップと対照させると

var tilename:Object = {
  1:'Sea', 3:'Grass', 5:'Sand', 7:'Player', 15:'Mountain', 17:'Event', 19:'Enemy1', 21:'Enemy2'
};

となるのか。

まずはFlashで表示を試す。Main.asを以下のように変更。Preloader.asは前回のまま。

Main.as

package {
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.events.Event;

    /**
     * ...
     * @author itouhiro
     */
    [SWF(width = "160", height = "160", frameRate = "30", backgroundColor = "0x888888")]
    public class Main extends Sprite {
        static public var isLoaded:Boolean = false;
        static public var maptip:Object;
        var tilename:Object = {1:'Sea', 3:'Grass', 5:'Sand', 7:'Player', 15:'Mountain', 17:'Event', 19:'Enemy1', 21:'Enemy2'};

        public function Main():void {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            maptip = { };
            var maptipNames:Array = [];
            for (var key in tilename) { maptipNames.push('Map' + tilename[key]); }
            var preload:Preloader = new Preloader(maptipNames);
            addChild(preload);
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
        }

        private function enterFrameHandler(e:Event):void {
            if ( ! isLoaded) return;
            removeEventListener(Event.ENTER_FRAME, enterFrameHandler);

            var mapData:Array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 3, 17, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, 3, 3, 1, 1, 3, 3, 15, 15, 3, 3, 3, 3, 1, 3, 3, 15, 15, 3, 1, 1, 3, 15, 15, 17, 15, 3, 3, 3, 3, 3, 3, 3, 15, 5, 1, 1, 3, 15, 15, 5, 5, 15, 3, 3, 3, 3, 3, 15, 15, 5, 1, 1, 3, 3, 15, 5, 5, 15, 15, 3, 3, 3, 3, 15, 17, 5, 1, 1, 1, 3, 15, 5, 5, 5, 5, 3, 3, 3, 15, 15, 5, 5, 1, 1, 1, 1, 15, 5, 5, 5, 5, 3, 3, 3, 3, 15, 15, 15, 1, 1, 1, 3, 15, 15, 5, 5, 3, 3, 3, 3, 17, 3, 3, 15, 1, 1, 1, 3, 17, 15, 15, 3, 3, 1, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, 5, 3, 15, 15, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 3, 15, 1, 1, 15, 15, 1, 15, 15, 1, 1, 1, 3, 5, 5, 5, 3, 3, 1, 15, 5, 5, 5, 15, 17, 15, 1, 1, 1, 3, 3, 5, 5, 5, 5, 5, 5, 15, 5, 5, 5, 15, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
            var mapWidth:int = 16;
            var map:Sprite = new Sprite();
            var mc:MovieClip;
            for (var ay:int = 0; ay < mapWidth; ay++) {
                for (var ax:int = 0; ax < mapData.length/mapWidth; ax++) {
                    mc = new maptip['Map'+tilename[mapData[ay*mapWidth+ax]]];
                    mc.x = ax * 32;
                    mc.y = ay * 32;
                    map.addChild(mc);
                }
            }
            addChild(map);

            mc = new maptip['MapPlayer'];
            mc.x = 96;
            mc.y = 32;
            addChild(mc);
        }
    }
}

実Flash。まだ操作不可。

修正する。

  • 160x160pxでは表示範囲が狭いので、480x480pxのウィンドウにする。
  • マップの外は海にする。
  • カーソルキーで動作可能。
  • 当たり判定をつける(海や山には入れない)。

Main.as

package {
    import flash.display.MovieClip;
    import flash.display.SpreadMethod;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;

    /**
     * ...
     * @author itouhiro
     */
    [SWF(width = "480", height = "480", frameRate = "30", backgroundColor = "0x888888")]
    public class Main extends Sprite {
        static public var isLoaded:Boolean = false;
        static public var maptip:Object;
        private var tilename:Object = { 1:'Sea', 3:'Grass', 5:'Sand', 7:'Player', 15:'Mountain', 17:'Event', 19:'Enemy1', 21:'Enemy2' };
        private var map:MovieClip;
        private var player:Sprite;

        public function Main():void {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            maptip = { };
            var maptipNames:Array = [];
            for (var key in tilename) { maptipNames.push('Map' + tilename[key]); }
            var preload:Preloader = new Preloader(maptipNames);
            addChild(preload);
            addEventListener(Event.ENTER_FRAME, initHandler);
        }

        private function initHandler(e:Event):void {
            if ( ! isLoaded) return;
            removeEventListener(Event.ENTER_FRAME, initHandler);
            if ( ! map) mapInit();
            addChild(map);
            player = new maptip['MapPlayer'];
            player.x = 480/2-16;
            player.y = 480/2-16;
            addChild(player);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
            addEventListener(Event.ENTER_FRAME, worldHandler);
            //trace('initHandler');
        }

        private function keyDownHandler(e:KeyboardEvent):void {
            if (map.nx !== 0 || map.ny !== 0) return;
            var step:int = 32;
            if      (e.keyCode === Keyboard.UP) map.ny = step;
            else if (e.keyCode === Keyboard.DOWN) map.ny = -step;
            else if (e.keyCode === Keyboard.RIGHT) map.nx = -step;
            else if (e.keyCode === Keyboard.LEFT) map.nx = step;
            //trace('keyDownHandler e.keyCode:'+e.keyCode);
        }

        private function worldHandler(e:Event):void {
            if (map.nx === 0 && map.ny === 0) return;
            //trace('worldHandler');
            var step:int = 4;
            if (map.nx) {
                if (Math.abs(map.nx) === 32) {
                    var nextX = map.nx > 0 ? 1 : -1;
                    if (isBlock(map.tile[map.py][map.px - nextX])) {
                        map.nx = 0;
                        return;
                    }
                }
                map.x += map.nx > 0 ? step : -step;
                var dx = map.nx; //delta x
                map.nx -= map.nx > 0 ? step : -step;
                if (map.nx === 0) { map.px -= dx > 0 ? 1 : -1; trace('player:' + map.px +','+ map.py);}
            }
            if (map.ny) {
                if (Math.abs(map.ny) === 32) {
                    var nextY = map.ny > 0 ? 1 : -1;
                    if (isBlock(map.tile[map.py - nextY][map.px])) {
                        map.ny = 0;
                        return;
                    }
                }
                map.y += map.ny > 0 ? step : -step;
                var dy = map.ny;
                map.ny -= map.ny > 0 ? step : -step;
                if (map.ny === 0) { map.py -= dy > 0 ? 1 : -1; trace('player:' + map.px +','+ map.py);}
            }
        }

        private function isBlock(tile:int):Boolean {
            if (tile === 17) {
                doEvent();
                return false;
            }else if (tile === 1 || tile === 15) {
                return true;
            }else {
                return false;
            }
        }

        private function doEvent():void {
            return;
        }

        private function mapInit():void {
            map = new MovieClip();
            map.tile = [];
            var mapData:Array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 3, 17, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, 3, 3, 1, 1, 3, 3, 15, 15, 3, 3, 3, 3, 1, 3, 3, 15, 15, 3, 1, 1, 3, 15, 15, 17, 15, 3, 3, 3, 3, 3, 3, 3, 15, 5, 1, 1, 3, 15, 15, 5, 5, 15, 3, 3, 3, 3, 3, 15, 15, 5, 1, 1, 3, 3, 15, 5, 5, 15, 15, 3, 3, 3, 3, 15, 17, 5, 1, 1, 1, 3, 15, 5, 5, 5, 5, 3, 3, 3, 15, 15, 5, 5, 1, 1, 1, 1, 15, 5, 5, 5, 5, 3, 3, 3, 3, 15, 15, 15, 1, 1, 1, 3, 15, 15, 5, 5, 3, 3, 3, 3, 17, 3, 3, 15, 1, 1, 1, 3, 17, 15, 15, 3, 3, 1, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3, 5, 3, 15, 15, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 3, 15, 1, 1, 15, 15, 1, 15, 15, 1, 1, 1, 3, 5, 5, 5, 3, 3, 1, 15, 5, 5, 5, 15, 17, 15, 1, 1, 1, 3, 3, 5, 5, 5, 5, 5, 5, 15, 5, 5, 5, 15, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
            var mapWidth:int = 16;

            var mc:MovieClip;
            for (var ay:int = -12; ay < mapWidth + 12; ay++) {
                if (ay >= 0 && ! map.tile[ay]) map.tile[ay] = [];
                for (var ax:int = -12; ax < (mapData.length / mapWidth) + 12; ax++) {
                    if (ax < 0 || ay < 0 || ax >= mapWidth || ay * mapWidth + ax >= mapData.length) {
                        mc = new maptip['MapSea'];
                    }else{
                        mc = new maptip['Map' + tilename[mapData[ay * mapWidth + ax]]];
                        map.tile[ay][ax] = mapData[ay * mapWidth + ax];
                    }
                    mc.x = ax * 32;
                    mc.y = ay * 32;
                    map.addChild(mc);
                }
            }
            map.px = 13; //player x(tile num)
            map.py = 10;
            map.x = (7 - map.px) * 32;
            map.y = (7 - map.py) * 32;
            map.nx = 0; // next moving x delta
            map.ny = 0;
        }
    }
}

実Flash。操作可。

次。立ち止まったとき、ステータスウィンドウを表示。 まだフォントはlocalにある(OSの)フォントを使用している(つまりlocalにインストールされていないとフォントが別ので表示される)。

--- a/src/Main.as
+++ b/src/Main.as
@@ -1,9 +1,13 @@
 package {
+    import flash.display.GradientType;
+    import flash.display.Graphics;
     import flash.display.MovieClip;
     import flash.display.SpreadMethod;
     import flash.display.Sprite;
     import flash.events.Event;
     import flash.events.KeyboardEvent;
+    import flash.text.TextField;
+    import flash.text.TextFormat;
     import flash.ui.Keyboard;
 
     /**
@@ -14,9 +18,11 @@
     public class Main extends Sprite {
         static public var isLoaded:Boolean = false;
         static public var maptip:Object;
-        var tilename:Object = { 1:'Sea', 3:'Grass', 5:'Sand', 7:'Player', 15:'Mountain', 17:'Event', 19:'Enemy1', 21:'Enemy2' };
-        var map:MovieClip;
-        var player:Sprite;
+        private var tilename:Object = { 1:'Sea', 3:'Grass', 5:'Sand', 7:'Player', 15:'Mountain', 17:'Event', 19:'Enemy1', 21:'Enemy2' };
+        private var map:MovieClip;
+        private var player:MovieClip;
+        private var statusWindow:Sprite;
+        private var isDisplayStatus:Boolean;
 
         public function Main():void {
             if (stage) init();
@@ -28,7 +34,7 @@
             // entry point
             maptip = { };
             var maptipNames:Array = [];
-            for (var key in tilename) { maptipNames.push('Map' + tilename[key]); }
+            for (var key:String in tilename) { maptipNames.push('Map' + tilename[key]); }
             var preload:Preloader = new Preloader(maptipNames);
             addChild(preload);
             addEventListener(Event.ENTER_FRAME, initHandler);
@@ -41,8 +47,15 @@
             addChild(map);
             player = new maptip['MapPlayer'];
             player.x = 480/2-16;
-            player.y = 480/2-16;
+            player.y = 480 / 2 - 16;
+            player.name = 'えにくす';
+            player.level = 1;
+            player.energy = 99;
+            player.magic = 12;
+            player.exp = 5;
             addChild(player);
+            player.stopCount = 0;
+            statusWindow = null;
             stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
             addEventListener(Event.ENTER_FRAME, worldHandler);
             //trace('initHandler');
@@ -59,21 +72,26 @@
         }
 
         private function worldHandler(e:Event):void {
+            player.stopCount++;
+            if (player.stopCount > 60 && ! statusWindow) displayStatus();
+
             if (map.nx === 0 && map.ny === 0) return;
+            player.stopCount = 0;
+            if (statusWindow) { removeChild(statusWindow); statusWindow = null; }
             //trace('worldHandler');
             var step:int = 4;
             if (map.nx) {
                 if (Math.abs(map.nx) === 32) {
-                    var nextX = map.nx > 0 ? 1 : -1;
+                    var nextX:int = map.nx > 0 ? 1 : -1;
                     if (isBlock(map.tile[map.py][map.px - nextX])) {
                         map.nx = 0;
                         return;
                     }
                 }
                 map.x += map.nx > 0 ? step : -step;
-                var dx = map.nx; //delta x
+                var dx:int = map.nx; //delta x
                 map.nx -= map.nx > 0 ? step : -step;
-                if (map.nx === 0) { map.px -= dx > 0 ? 1 : -1; trace('player:' + map.px +','+ map.py);}
+                if (map.nx === 0) { map.px -= dx > 0 ? 1 : -1; trace('player:' + map.px +',' + map.py); }
             }
             if (map.ny) {
                 if (Math.abs(map.ny) === 32) {
@@ -90,6 +108,41 @@
             }
         }
 
+        private function displayStatus():void {
+            statusWindow = new Sprite();
+            var g:Graphics = statusWindow.graphics;
+            g.beginFill(0x000000, 0.7);
+            g.lineStyle(2, 0xffffff);
+            g.drawRoundRect(0, 0, 120, 168, 15, 15);
+            g.endFill();
+            statusWindow.x = 25;
+            statusWindow.y = 20;
+            var msg:Array = [player.name, 'レベル ' + fmtNum(player.level, 2), 'HP ' + fmtNum(player.energy, 3), 'MP ' + fmtNum(player.magic, 3), 'E ' + fmtNum(player.exp, 4)];
+            for (var i:int = 0; i < msg.length; i++) {
+                putChars(msg[i], statusWindow, 8, i*32);
+            }
+            addChild(statusWindow);
+        }
+
+        private function putChars(msg:String, theWindow:Sprite, locX:int, locY:int):void {
+            var chars:TextField = new TextField();
+            chars.defaultTextFormat = new TextFormat('Ueten 2m medium', 18, 0xffffff);
+            chars.text = msg;
+            chars.x = locX;
+            chars.y = locY;
+            theWindow.addChild(chars);
+        }
+
+        private function fmtNum(num:int, len:int):String {
+            var han2zen:Object = { 0:'0', 1:'1', 2:'2', 3:'3', 4:'4', 5:'5', 6:'6', 7:'7', 8:'8', 9:'9' };
+            var numZen = '        ';
+            var numHan = num.toString();
+            for (var i:int = 0; i < numHan.length; i++) {
+                numZen += han2zen[numHan.charAt(i)];
+            }
+            return numZen.substr( -len);
+        }
+
         private function isBlock(tile:int):Boolean {
             if (tile === 17) {
                 doEvent();

0 件のコメント:

コメントを投稿

人気記事