プログラミング工房
TOP
Flex基本
開発環境 JavaScript連携 PHP連携1 PHP連携2(AMFPHP) ウィンドウ ボタンスキン 矩形スキン ローカルファイル(テキスト) F5等の対策 ローカルファイル(イメージ) ダウンロード、アップロード 1枚の画像のカラーを変更 時間のかかる計算処理 外部SWFの読込み
Flexで3D
Flexだけで3D Papervision3Dを使ってみる 3Dオブジェクト カメラ、前後判定の工夫しました 自由な形状を作成
Flexでクラス
Class1(白黒ゲームの盤) Class2(白黒ゲームのプレイヤー) Class3(プレイヤーを外部SWF)
PHP
共通関数1
Flexの作品
お問合せの説明 サンプルのソース表示の説明 分子構造の表示(PDBファイル) マンデルブロ集合の画像作成 swf参加型白黒ゲーム(Reversi) ストップウォッチ WEB素材
AIR
AIRを使ってみる ソースファイルのHTML変換を作る
etc.
マンデルブロ集合のギャラリー ジュリア集合のギャラリー wonderflを使ってみました お問合せ

Flexでクラス-Class1(白黒ゲームの盤)

■2011.04.15:作成
■2011.04.20:改定
サンプル サンプルのソース

Flexでクラスを作ってみる。

今までクラスを考慮していなかったので、クラスを使ってみる。

白黒ゲーム(Reversi)の盤のクラスと共通関数のクラスを作てみた。
ただし、ゲームを作ることでなくクラスを作ることが目的なので、綺麗さ等は考慮していません。
(第一弾としてプレイヤーは白黒とも人間です、二人いれば交互に打つこともできますが、 テストでは一人で白黒を打って確認しています。)

クラスは「src」フォルダの下に「cls」フォルダを作成してそこにクラスファイルをおくことにします。 (「package」記述の後ろに「cls」が必要になり「package cls」と記述します)

白黒ゲーム(Reversi)の共通のクラス clsRevCom.as

共通関数のクラス「clsRevCom」は、どこからでも自由に使えるクラスにします。
他の言語と同じように関数等の宣言の前に「static」をつけます。

関数は以下のような物を用意します。
 clsRevCom.getYokoNam(iti:int):String //升目の横方向の名称["a", "b", ・・, "h" ]
 clsRevCom.getTateNam(iti:int):String //升目の縦方向の名称["1", "2", ・・, "8" ]
 clsRevCom.getYokoIti(itiNam:String):int //升目の名称より横方向の位置を求める
 clsRevCom.getTateIti(itiNam:String):int //升目の名称より縦方向の位置を求める
 clsRevCom.getPlColNam(ip:int):String //プレイヤーの色名称(黒、白)
 clsRevCom.getFirstPlayer():int //最初に打つプレイヤー
 clsRevCom.getOtherPlayer(ip:int):int //相手のプレイヤー
package cls {
    //黒白ゲーム(リバーシ)
    //共通関数
    public class clsRevCom {
        //枡目の名称
        static private const arrYokoName:Array = 
              ["a", "b", "c", "d", "e", "f", "g", "h" ];
        static private const arrTateName:Array =
             ["1", "2", "3", "4", "5", "6", "7", "8" ];

        //升目の横方向の名称
        static public function getYokoNam(iti:int):String {
            var strRet:String = "";
            if (iti >= 0 && iti < arrYokoName.length) 
                                { strRet = arrYokoName[iti];}
            return strRet;
        }
        //升目の縦方向の名称
        static public function getTateNam(iti:int):String {
            var strRet:String = "";
            if (iti >= 0 && iti < arrTateName.length) 
                               { strRet = arrTateName[iti];}
            return strRet;
        }
             :

白黒ゲーム(Reversi)の盤のクラス clsBoard.as

基底クラスを「Sprite」にします。
 盤の表示、マウスイベント「onMouseDown」をクラス内で処理することが可能になります。
コンストラクター「clsBoard()」と初期化関数「init(e:Event = null)」について。
 基底クラスに「Sprite」を使用するときはこの形になるようです。
 多分「Sprite」の準備が整うまで初期処理を待つためだと思います。
package cls {
    //黒白ゲーム(リバーシ)
    //盤クラス(ボードとルール)
    import flash.display.Sprite;
         :
    public class clsBoard extends Sprite {
        //盤データ(先手黒:1、後手白:2、未配置:0、盤の外:-1)
        //initDataSet()関数を参照
        private var boardData:Array;
               :
        //コンストラクター================================================
        public function clsBoard():void {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        //初期化
        private function init(e:Event = null):void {
            disText();
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            initDataSet();
        }

盤をマウスクリックしたときの処理について
 マウスでクリックした時の関数を登録できるようにします。
 クリックした升目("a1"~"h8")をクラスを利用する側に知らせることができます。
        //盤をクリックしたときに呼び出す関数の登録==============================
        public function fncSetFnc(fnc:Function):void {
            fncMouseDown = fnc;
        }
        //盤をクリックしたときの処理
        private function onMouseDown(event:MouseEvent):void {
            if(fncMouseDown==null) { return;}
            var itiX:int = mouseX / mSize;
            var itiY:int = mouseY / mSize;
            if (itiX <= 0 || itiX > 8 || itiY <= 0 || itiY > 8) { return;}
            fncMouseDown(clsRevCom.getYokoNam(itiX-1)+
                             clsRevCom.getTateNam(itiY-1));
        }

盤の駒のデータは次の形で持ちます。
「-1」は盤の外側をあらわします、これがあると「駒を置けるか」等の処理が楽になります。
        //盤の初期化==================================================
        public function initDataSet():void {
            var ip1:int = clsRevCom.getFirstPlayer();
            var ip2:int = clsRevCom.getOtherPlayer(ip1);
            boardData = new Array(
                new Array( -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
                new Array( -1, 0, 0, 0, 0, 0, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, 0, 0, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, 0, 0, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, ip2, ip1, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, ip1, ip2, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, 0, 0, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, 0, 0, 0, 0, 0, -1),
                new Array( -1, 0, 0, 0, 0, 0, 0, 0, 0, -1),
                new Array( -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
            arrRireki = new Array();
            disBoard();
        }

「駒を置けるか」のチェックの説明
 指定升目が未配置(=0)かチェックする。
 上下左右斜めの「8方向」に対して「相手の駒がある」とき以下の処理を行う
  指定方向(idx,idx)の次の駒が「相手の駒の時」はその次をチェック(再起関数)
  指定方向の次の駒が「自分の駒の時」、配置可能で終了
  指定方向の次の駒が「未配置(=0)」または「盤の外(=-1)」のときこの方向はNG
(「指定升目に駒を置く」okuIti関数も同様な処理です。)
        //指定升目に打てるかチェック=====================================
        // itiNam:升目の名称("a1"~"h8")
        // ip:プレイヤー(1,2)
        // 戻り値:反転する升目数+1(打った駒)、0=置けません
        public function chkIti(itiNam:String, ip:int):int {
            var intRet:int = 0;
            var itiX:int = clsRevCom.getYokoIti(itiNam)+1;
            var itiY:int = clsRevCom.getTateIti(itiNam)+1;
            if (itiX == 0 || itiY == 0) { return intRet; }
            intRet = chkItiXY(itiX, itiY, ip);
            return intRet;
        }
        // itiX,itiY:チェックする升目
        // ip:プレイヤー
        // 戻り値:反転する升目数+1(打った駒)、0=置けません
        private function chkItiXY(itiX:int,itiY:int, ip:int):int {
            var intRet:int = 0;
            var ip2:int =clsRevCom.getOtherPlayer(ip);
            //指定升目に何もない
            if (boardData[itiX][itiY] != 0) { return intRet; }
            //8方向をチェック、まず敵の色(繰り返し)、自分の色のチェックを行う
            for (var idx:int = -1; idx <= 1; idx++) {
                for (var idy:int = -1; idy <= 1; idy++) {
                    if (idx != 0 || idy != 0) {
                        if (boardData[itiX+idx][itiY+idy] == ip2) {
                            var n:int = chkItiXYnext(itiX+idx, itiY+idy, 
                                      idx, idy, ip,ip2);
                            if( n>0) { intRet+=n;}
                        }
                    }
                }
            }
            if (intRet > 0) { intRet++;}
            return intRet;
        }
        //指定升目に打てるかチェック(再起関数)
        // itiX,itiY:チェックする升目
        // idx,idy:チェックする方向(8方向の1方向)
        // ip,ip2:自分と相手
        // 戻り値:true:配置できる
        private function chkItiXYnext(itiX:int, itiY:int, 
                idx:int, idy:int, ip:int, ip2:int):int {
            var intRet:int = 0;
            //自分の色ならOK
            if (boardData[itiX + idx][itiY + idy] == ip) { 
                intRet = 0;
            //敵の色なら次をチェック
            } else if(boardData[itiX + idx][itiY + idy] == ip2) { 
                intRet=chkItiXYnext(itiX+idx, itiY+idy, idx, idy, ip, ip2);
            } else {
                //その他(空、盤の外)ならNG
                intRet = -1;
            }
            if (intRet >= 0) { 
                intRet++;
            }
            return intRet;
        }


それ以外に以下のような関数を用意
 指定プレーヤに打つ場所があるか:すべての升目に配置できるかチェック
 指定プレーヤの駒数
 指定位置の枡の状態
 打った升目の履歴の取得
        //指定プレーヤに打つ場所があるか=======================================
        // ip:プレイヤー
        // 戻り値:打つ場所の有(true)/無(false)
        public function chkPlayer(ip:int):Boolean {
            for (var itiX:int = 1; itiX < 9; itiX++) {
                for (var itiY:int = 1; itiY < 9; itiY++) {
                    if (chkItiXY(itiX, itiY, ip) >0) { return true; }
                }
            }
            return false;
        }
        //指定プレーヤ、未配置の駒数============================================
        // ip:プレイヤー(1,2)、0で未配置
        // 戻り値:駒数
        public function Kekka(ip:int):int {
            var kazu:int = 0;
            for (var itiX:int = 1; itiX < 9; itiX++) {
                for (var itiY:int = 1; itiY < 9; itiY++) {
                    if (boardData[itiX][itiY] == ip) { kazu++;}
                }
            }
            return kazu;
        }
        //指定位置の枡の状態================================================
        // itiNam:升目の名称("a1"~"h8")
        //  戻り値:黒(1)、白(2)、未(0)、升目不正(-1)
        public function masuJyoutai(itiNam:String):int {
            var itiX:int = clsRevCom.getYokoIti(itiNam)+1;
            var itiY:int = clsRevCom.getTateIti(itiNam)+1;
            if (itiX == 0 || itiY == 0) { return -1; }
            return boardData[itiX][itiY];
        }
        //打った升目の履歴の取得================================================
        // no:no回前(0が一回前)の履歴を取得
        // 戻り値:履歴{Player:プレイヤー(1,2),MasuIti:升目名("a1","b5"),
        KomaSuu:反転した数+1}
        //      noが正しくないときnull
        public function getRireki(no:int):Object {
            var objRet:Object = null;
            if (no >= 0 && no < arrRireki.length) {
                objRet = arrRireki[arrRireki.length-no-1];
            }
            return objRet;
        }


クラスを使う側

まず使用する盤クラス、をインポート「import cls.*;」します。
あとは普通のコンテンツのように使います。
プレイヤーを黒(ip = 1)として、盤がクリックした時の関数を定義
import mx.controls.Alert;
import cls.*;
//打つプレイヤー(1:黒先行、2:白後攻)
private var ip:int;
//ボードクラス(ボードとルール)
private var objBoard:clsBoard;

private function initDataSet():void {
    objBoard = new clsBoard;
    base.addChild(objBoard);
    fncStart();
}
//ゲーム開始
public function fncStart(e:*= null):void {
    fncMess2Clear();
    fncMess2Set("盤の初期化");
    objBoard.initDataSet();
    ip = clsRevCom.getFirstPlayer();        //1:黒先行
    fncMess1Set(clsRevCom.getPlColNam(ip) + "が打つ番です。");
    //盤がクリックしたときの関数定義
    objBoard.fncSetFnc(fncOnxy);
}

プレイヤーが駒を打ったときの処理(全体の流れの処理を行っている部分です)
クリックした升目が駒を置ける場所なら(NGなら再度同じプレイヤーで実行)
 その升目に駒を置く(駒の裏返しも行う)
 次に打つプレイヤーを相手(もう一人)にします。
次に打つプレイヤーに打つところがあれば、そのプレイヤーが打つ人。
次に打つプレイヤーが打てない場合は前の(次の次の)プレイヤーが打つ

ただし、二人とも打てないとき終了(升目の空きの有無に関係なく)
//盤がクリックしたとき
// itiNam:クリックした升目("a1"~"h8")
public function fncOnxy(itiNam:String):void {
    var n:int = objBoard.okuIti(itiNam, ip);
    if (n > 0) {    //升目がOKか,駒を置く
        fncMess2Set(clsRevCom.getPlColNam(ip) + ":" + 
                    itiNam + ":" + (n-1) + "個変更");
        ip = clsRevCom.getOtherPlayer(ip);  //次に打つプレイヤー
    } else {
        return;     //打てない升目をクリック
    }
    //次に打つプレイヤーが打てる場所があるか?
    if ( objBoard.chkPlayer(ip) >0) {
        fncMess1Set(clsRevCom.getPlColNam(ip) + "が打つ番です。");
        return;
    }
    //次の次のプレイヤーが打てる場所があるか?
    ip = clsRevCom.getOtherPlayer(ip);  //次に打つプレイヤー
    if ( objBoard.chkPlayer(ip) >0) {
        fncMess1Set(clsRevCom.getPlColNam(ip) + "が打つ番です。");
        return;
    }
    //二人とも打てる場所なし->終了
    var ip1:int = clsRevCom.getFirstPlayer();
    var ip2:int = clsRevCom.getOtherPlayer(ip1);
    var ip1n:int = objBoard.Kekka(ip1);
    var ip2n:int = objBoard.Kekka(ip2);
    var strWin:String = "引き分けです。";
    if (ip1n > ip2n) { strWin = clsRevCom.getPlColNam(ip1)+"の勝ちです。"; }
    if (ip1n < ip2n) { strWin = clsRevCom.getPlColNam(ip2)+"の勝ちです。"; }
    
    Alert.show("終了しました。" +strWin+"\n"+ 
                              clsRevCom.getPlColNam(ip1) + ":" + ip1n +
                    "/" + clsRevCom.getPlColNam(ip2) + ":" + ip2n,
                    "Reversi1",mx.controls.Alert.OK,this,fncStart);
    objBoard.fncSetFnc(null);   //盤のクリック無効
}

プログラミング工房