flash on 2011-10-7

by harupiyo
キャラクターの位置を基準とした画面スクロール処理のスケッチ

仕様
====

世界全体の広さを200x200 とする
世界全体は、縮小表示して画面いっぱいに表示されることもあるし(キャラクターがどこに居ようとスクロールの必要なし)、一部がズームアップして拡大されることもある(スクロール処理が必要)
世界の4隅に来た場合は行き止まり

スクロール処理
==============

表示画面が世界全体の一部である時はスクロール処理が必要
世界の左上の行き止まり処理をするために、キャラクターが2 の位置に来た時にスクロールを開始するようマージンをとる
[世 界 全 体]                  
margine
+-------|-------+------------------+      
|1              |                  |      
|               |                  |      
margine -       2       |                  |      
|               |                  |      
|   [表示画面]  |                  |      
+---------------+                  |      
|                                  |      
|                                  |
|                                  |
|                                  |
|                                  |
+----------------------------------+
世界の右下の行き止まり処理をするため、スクロールの最大量は下の図のA, B の範囲とし、上限を設定する
[世 界 全 体]
+----------------------------------+
|                    ^             |
|   [表示画面の   ]  |
♥0 | Line 108 | Modified 2011-10-17 14:35:31 | MIT License
play

ActionScript3 source code

/**
 * Copyright harupiyo ( http://wonderfl.net/user/harupiyo )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jwZy
 */

// キャラクターの位置を基準とした画面スクロール処理のスケッチ
// 
// 仕様
// ====
// 
//  * 世界全体の広さを200x200 とする
//  * 世界全体は、縮小表示して画面いっぱいに表示されることもあるし(キャラクターがどこに居ようとスクロールの必要なし)、一部がズームアップして拡大されることもある(スクロール処理が必要)
//  * 世界の4隅に来た場合は行き止まり
// 
// スクロール処理
// ==============
// 
//  * 表示画面が世界全体の一部である時はスクロール処理が必要
//  * 世界の左上の行き止まり処理をするために、キャラクターが2 の位置に来た時にスクロールを開始するようマージンをとる
//                     [世 界 全 体]                  
//                margine
//          +-------|-------+------------------+      
//          |1              |                  |      
//          |               |                  |      
//  margine -       2       |                  |      
//          |               |                  |      
//          |   [表示画面]  |                  |      
//          +---------------+                  |      
//          |                                  |      
//          |                                  |
//          |                                  |
//          |                                  |
//          |                                  |
//          +----------------------------------+
//  * 世界の右下の行き止まり処理をするため、スクロールの最大量は下の図のA, B の範囲とし、上限を設定する
//                     [世 界 全 体]
//          +----------------------------------+
//          |                    ^             |
//          |   [表示画面の   ]  |             |
//          |   [視点位置(*)が]  | A           |
//          |   [動く範囲     ]  |             |
//          |                    |             |
//          |                    v             |
//          |<- - - - - - - - - >*-------------+
//          |         B          |             |
//          |                    |  [表示画面] |
//          |                    |             |
//          |                    |             |
//          +--------------------+-------------+
// 
package {
    import flash.display.*;
    import flash.net.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.text.*;
    
    [SWF(width="200", height="200", frameRate="30", backgroundColor="0x000000")]
    
    public class FlashTest extends Sprite {

        private var camera:Camera;
        private var screen:Point;
        private var bg_container:Sprite;
        private var map:Loader;
        private var piece:Sprite;
        private var speed:int = 6;
        
        public function FlashTest() {
            camera = new Camera( 200, 200 );
            screen = new Point( 200, 200 );
            
            // 拡大縮小する面はこの1枚のスプライトとする。よって、拡大縮小の対象はすべてこの面に描画(addChild)すること.
            bg_container = new Sprite();
            addChild( bg_container );
            
            var tURL:String = "http://mii-tetu.cocolog-nifty.com/photos/uncategorized/2008/05/24/050_2.jpg";
            var urImage:URLRequest = new URLRequest( tURL );
            map = new Loader();
            map.load( urImage );
            var info:LoaderInfo = map.contentLoaderInfo;
            info.addEventListener(Event.COMPLETE, _init)

            // コマ(操作できるキャラクター)
            piece = new Sprite();
            piece.graphics.beginFill(0x0000ff);
            piece.graphics.drawRect(0, 0, 32, 32);    // 手抜きですんません
            piece.graphics.endFill();
            piece.x = 0;
            piece.y = 0;
            stage.addEventListener( KeyboardEvent.KEY_DOWN, _KeyDownFunc);  // MEMO: stage に対してaddEventListener しないといけないようだ. なぜ?
        }
        
        public function _init(evt:Event):void{
            map.width = 200;                // 画像が大きすぎるので適宜縮小
            map.height = 200;
            bg_container.addChild( map );
            bg_container.addChild(piece);    
            
            addEventListener( Event.ENTER_FRAME, _enterFrame );

            traceView();
        }

        private function _enterFrame(evt:Event):void {
            camera.view( bg_container, piece.x, piece.y );
        }

        private function _KeyDownFunc(event:KeyboardEvent):void{
            // キャラクターは、世界の大きさの範囲内で移動可能
            switch( event.keyCode ){
                case 37: piece.x-=speed; break;
                case 38: piece.y-=speed; break;
                case 39: piece.x+=speed; break;
                case 40: piece.y+=speed; break;
            }
            if( piece.x < 0) piece.x = 0;
            if( piece.y < 0) piece.y = 0;
            if( piece.x > screen.x - 32 ) piece.x = screen.x - 32;
            if( piece.y > screen.y - 32 ) piece.y = screen.y - 32;
        }

        private function traceView():void {
            var t:TextField = new TextField();
            t.autoSize = TextFieldAutoSize.LEFT;
            t.textColor = 0x00FF0000;
            stage.addChild(t);
            trace = function(s:String):void { t.appendText(s + "\n"); };
            cls = function():void { t.text = ""; };
        }
        private var trace:Function;
        private var cls:Function;
    }
}

import flash.display.*;
import flash.geom.*;

class Camera {
    private var screen:Point;
    private var cam:Vector3D;
    private var f:Number = 1;                                // 焦点距離(F) を今回は1 にする
    private var zmin:Number;
    private var margine:Point;
    private var sw:Boolean = true;

    public function Camera( width:int, height:int ):void {
        screen      = new Point( width, height );
        zmin        = -width;
        cam         = new Vector3D( 0, 0, zmin );                // カメラ位置。原点から-width 離れると全体表示。z の取りうる値は zmin から0 まで。
        margine     = new Point( width / 3, height / 3 );        // 画面の1/3 に来た時にスクロール開始するように、初期マージン位置の設定値を保存
    }

    public function enlargement_factor():Number{
        return f * screen.x / -cam.z;
    }

    // container: 表示する世界. ここではこれをどのくらいずらすか、拡大するかを計算する
    // PoG(Point ob Gaze: 注視点=キャラクターのいる位置)
    public function view( container:Sprite, x:Number, y:Number ):void {
        // テスト用の拡大縮小
        if(sw){
            cam.z++;
            if(cam.z >= -150) sw = false;
        }else{
            cam.z--;
            if(cam.z <= zmin) sw = true;
        }

        // 現在のcam.z 位置から見た時の拡大率(1.0~)
        var scale:Number = enlargement_factor();

        // その拡大率の時の世界の広さ
        var world:Point = new Point( screen.x * scale, screen.y * scale );
        
        // 拡大率を考慮してマージン幅の計算. この範囲でスクロールする
        var min_margine:Point = new Point( margine.x / scale, margine.y / scale );
        var max_margine:Point = new Point( screen.x - (screen.x / scale), screen.y - (screen.y / scale) );
        
        // PoG から、スクロール量を算出。ただし、マージン幅の制限を受ける
        var scroll_x:Number = (x - min_margine.x) < 0 ? 0 : x - min_margine.x;
        var scroll_y:Number = (y - min_margine.y) < 0 ? 0 : y - min_margine.y;
        if(scroll_x > max_margine.x ) scroll_x = max_margine.x;
        if(scroll_y > max_margine.y ) scroll_y = max_margine.y;
        
        // 算出した拡大率、スクロール量を設定
        container.scaleX = scale;
        container.scaleY = scale;
        container.x = -scroll_x * scale;
        container.y = -scroll_y * scale;

    }

}

Forked