忍者ブログ
なんとなくだらだらと。方向性はまだ決まってない。 当方のプログラムでは、山田巧さん作成のDXライブラリを利用させていただいてます。 本サイト http://homepage2.nifty.com/natupaji/DxLib/index.html DX Library Copyright (C) 2001-2008 Takumi Yamada.
[55] [54] [53] [52] [51] [50] [49] [48] [47] [46] [45]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

どげんかせんといけん。


v014

ダウンロード http://hiyayakko.sarashi.com/SimpleSoccer/SC13.zip (2009.08.21)



before
                if(player[i].getTeam()==BLUE) {
                    if(player[i].getPos()==GOALKEEPER) ball.Kick(600,GetRand(300),8.0);
                    else if(ball.getBx()>400) ball.Kick(600,GetRand(80)+110,6.0);
                    else ball.Kick(600,GetRand(300),4.0);
                } else {
                    if(player[i].getPos()==GOALKEEPER) ball.Kick(0,GetRand(300),8.0);
                    else if(ball.getBx()<200) ball.Kick(0,GetRand(80)+110,6.0);
                    else ball.Kick(0,GetRand(300),4.0);
                }

プレイヤー[i]のチームが青チームのとき
ゴールキーパーなら、ボールを座標(600,0~300)へ力8.0で蹴り返す
ボール座標が400以上、相手ゴールに十分近いときは、座標(600,110+0~80)
すなわち座標(600,110~190)、相手ゴールへ目掛けて強さ6.0で蹴る
それ以外、一般プレイヤーがゴールを狙えない状況なら(600,0~300)へ力4.0で
とりあえず敵陣に向けて蹴り返す。
青チームじゃないとき(=赤チーム)
ゴールキーパーなら、ボールを座標(0,0~300)へ力8.0で蹴り返す
ボール座標が200以下、相手ゴールに十分近いとき、座標(0,110+0~80)
すなわち座標(0,110~190)、相手ゴールへ目掛けて強さ6.0で蹴る
それ以外、一般プレイヤーがゴールを狙えない状況なら(0,0~300)へ力4.0で
とりあえず敵陣に向けて蹴り返す。

日本語で書いたらこんな感じ?
とにかく固定の数字(マジックナンバー)が多い。
このうっとおしい事態を打破するために、Teamクラスなんて作ってみた。
設計上必要だからではなく、付け焼刃でとりあえず追加みたいな感じだから存在が浮いてる。
今のところ?

今のところ、目的は固定の数字と赤軍青軍で同じ処理を繰り返しているのをまとめるため
だけだから、大量の定数を初期化で持たせてるだけ。
無駄にきれいなソース。

Team.h


// Team.h
#pragma once

class Team
{
    int m_EnemyGoalLine;        // 相手ゴールライン
    int m_KeepArea_Left;        // 自陣左端
    int m_KeepArea_Right;       // 自陣右端
    int m_AttackArea_Left;      // 攻撃ポイント左端
    int m_AttackArea_Right;     // 攻撃ポイント右端
public:
    void Init(int EnemyGoalLine,
              int AttackArea_Left,
              int AttackArea_Right,
              int KeepArea_Left,
              int KeepArea_Right);
    int EnemyGoalLine(){return m_EnemyGoalLine;}
    int KeepArea_Left(){return m_KeepArea_Left;}
    int KeepArea_Right(){return m_KeepArea_Right;}
    int AttackArea_Left(){return m_AttackArea_Left;}
    int AttackArea_Right(){return m_AttackArea_Right;}

};

void Team::Init(int EnemyGoalLine,
                int AttackArea_Left, int AttackArea_Right,
                int KeepArea_Left, int KeepArea_Right)
{
    m_EnemyGoalLine    = EnemyGoalLine;
    m_AttackArea_Left  = AttackArea_Left;
    m_AttackArea_Right = AttackArea_Right;
    m_KeepArea_Left    = KeepArea_Left;
    m_KeepArea_Right   = KeepArea_Right;

}



さっき和訳したプログラムから、0とか600の数字をそれっぽい名前に変える。
突然メンバ変数に m_ なんてつけてみる。
こういうことは開発の途中でやることじゃない。やるなら徹底的に全クラス書き換えて
統一しておくべき。精神論。

0、600 は相手のゴールライン=自軍の攻める目標ということで問題なく。
次の400以上とか200以下とか、プログラムの処理でどうまとめられるか考える。
青軍なら攻撃ライン400以上とかって if 文で場合分けしたら今までと変わらないじゃん。
小一時間悩んだ結果、範囲指定でいいことに気が付いた。
青軍なら 400~600 の間、赤軍なら 0~200 の間って感じで指定してみる。
この方法なら赤青で場合分けしなくても、両軍どちらからでも同じ if 文ひとつで使いまわせる。

そんな感じの思考回路で組んでみた。
ちょっと必要以上に丁寧にやりすぎた感が大きい。

main.cpp


        // ボールを蹴られる位置にいるか。
        tm = player[i].getTeam();
        dist = Distance(player[i], ball);
        if(((player[i].getPos()==GOALKEEPER)&&(dist<81))||(dist<25)){
            // ゴールキーパーは距離81、一般プレイヤーは距離25以下で蹴り返すことができる
            if((player[i].getPos()==GOALKEEPER)||(ball.getSpd()<2.0)){
                // ボールの近くにいて、なおかつボールスピードが低いとき
                // スピード判定は、重なってるプレイヤー同士でもみもみして後攻有利になるので。
                if(player[i].getPos()==GOALKEEPER)
                    ball.Kick(team[tm].EnemyGoalLine(),GetRand(300),7.0);
                else {
                    if((ball.getBx() > team[tm].AttackArea_Left())&&(ball.getBx() < team

[tm].AttackArea_Right()))
                        ball.Kick(team[tm].EnemyGoalLine(), GetRand(80)+110, 6.0);
                    else ball.Kick(team[tm].EnemyGoalLine(), GetRand(300),4.0);
                }

/*
                if(player[i].getTeam()==BLUE) {
                    if(player[i].getPos()==GOALKEEPER) ball.Kick(600,GetRand(300),8.0);
                    else if(ball.getBx()>400) ball.Kick(600,GetRand(80)+110,6.0);
                    else ball.Kick(600,GetRand(300),4.0);
                } else {
                    if(player[i].getPos()==GOALKEEPER) ball.Kick(0,GetRand(300),8.0);
                    else if(ball.getBx()<200) ball.Kick(0,GetRand(80)+110,6.0);
                    else ball.Kick(0,GetRand(300),4.0);
                }
*/
                //for(int j=0;j<10;j++)player[j].setStatus(WAIT);
            }
        }



これを使うための定数宣言を、ゲームの初期化で。

main.cpp

#include "DxLib.h"
#include <vector>
#include <math.h>

using namespace std;

const char* WindowText = "SimpleSoccer";

#include "Game.h"
#include "Team.h"
#include "Player.h"
#include "Ball.h"


// グローバル変数(>_<)
int count;

// グローバルクラス定義(>_<)
Player player[10];
Ball ball;
Game game;
Team team[2];
    :
    :

int GameInit()
{
    game.Init();
    team[BLUE].Init( 600,               // EnemyGoalLine
                     400, 600,          // AttackArea Left-Right
                       0, 200 );        // KeepArea   Left-Right

    team[ RED].Init(   0,               // EnemyGoalLine
                       0, 200,          // AttackArea Left-Right
                     400, 600 );        // KeepArea   Left-Right

    player[0].Init(BLUE,   ATTACKER, 300, 150, 250,  50);
    player[1].Init(BLUE,   ATTACKER, 300, 150, 250, 250);
    :
    :



Team の初期化がどうしても定数の列挙になってしまうので、こうやって資料作って
おかないと、後からわからなくなるんじゃないかと懸念。


v015

v016


俺の美的センス的に、かなり良い出来に収まった。
クラスまで作って、ここまでキレイに収めるのはやりすぎじゃないかという不安は
常につきまとっていたけど、苦労しただけの価値は十分以上にある。


何が良いかって言ったら、なんと言っても仕様変更に強い。
これに尽きる。
ピッチの幅を広げたいとか思いついたときに、GameInit() で定義してる定数を
変えるだけで、本体には手をつけずに変更完了できる。
なんなら、この設定を外部ファイルにしてしまったらプログラマが変更に関わる必要が
まったくなくなる。
今のところ、この「蹴り返し」判定部分だけの話なんだけど、この思想がシステム全域に
渡ったら、プログラマ中級レベル卒業、黒帯有段者レベル名乗ってもいいんじゃないかと。

免許皆伝級の超上級マイスターなら、最初期の設計段階からこの手の仕様変更の影響が
プログラム全域に及ばないように意識して組み立てる。
ちょっと自分のブログ読み返したら、サッカー第1回の時点で設計挫折して、いきあたり
ばったりでもとにかく動くもの作りたいって意思表示してた。マジウケ。
いろいろ考えて動けないのも、行き当たりばったりで突き進むのも同じ俺。かわいい。


しばらくは、この手のプログラム整理(リファクタリング)が続くと思う。
早く先へ進みたい気持ちで溢れてるだろうけど、今気になってるポイントは今しか直せない。
たぶん心が一度離れた後では、適当にだらっと書いたソースそのままでは、2度と
読む気が出ないだろう。


この場所、ここへ至る道に、if(player[i].getPos()==GOALKEEPER) の判定が3回連なってる
のが気になるとか。
少なくとも、この GamePlay() 関数からマジックナンバーを追い出して、ピッチ幅の変更なんか
では揺るがないような設計の関数にしたいとか。
そもそも、GamePlay() 関数の仕事が多すぎるから、どうにかして関数を分割してみるとか
クラスインスタンスが請け負うべき仕事はクラスインスタンスに任せるとか。

クラス定義的には、GameクラスがTeamクラス赤青ふたつのインスタンスを所有し、
そのTeamクラスが各プレイヤーを人数分所有するとか、そういうふうに作るべき。
赤チーム青チームの司令塔、戦略担当はTeamクラスが受け持つべきで、
グローバルクラスに頼って力技で全体を支配していいものではない。
こういう作り直しに近い全体に及ぶ変更をしてしまう覚悟をつけるには、もうちょい
座禅・瞑想で精神統一して覚悟を決めないとムリかな。



ブログエントリ シンプルサッカー(1)
http://hiyayakkoprog.blog.shinobi.jp/Entry/38/

ダウンロードHP
http://hiyayakko.sarashi.com/SimpleSoccer/SimpleSoccer.html

したらば掲示板  シンプルサッカー
http://jbbs.livedoor.jp/bbs/read.cgi/computer/42268/1250213377/l50
PR


忍者ブログ [PR]
カレンダー
03 2025/04 05
S M T W T F S
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
フリーエリア
バーコード
ブログ内検索
P R
アクセス解析
カウンター