いやあタイヘンだった。
原因不明バグで鼻毛に白髪が混じったよ。悩みすぎて、マジで禿げるかと思った。
いろいろ見直したりしながら遊んでたら、けっこう進んでしまった気がする。
ダウンロード http://hiyayakko.sarashi.com/Simp leSoccer/SC05.zip (2009.08.14)
ゴールライン通過の処理を入れる。
入れるところはボール移動のところ一択だよな。
ball.h
bool Ball::Move()
{
Bx += Vx*Spd;
By += Vy*Spd;
Spd *= 0.98; // 地面との摩擦により、転がるスピードが落ちる
if(Spd < 0.001) { Spd = 0.0; Vx = Vy = 0.0;}
// 壁の反射とゴール判定
if(By<0.0){Vy=-Vy;By=0.0;}
if(By>300.0){Vy=-Vy;By=300.0;}
if((Bx<0.0)||(Bx>600.0)){
Vx=-Vx;
if(Bx<300.0) Bx=0.0;
else Bx=600.0;
if((By>=100.0)&&(By<=200.0)){
return true;
}
}
return false;
}
とは言いながら、ここ、ボールの移動処理に「ゴールが決まって点数が入ってプレイヤーを
初期位置に戻す」なんて処理は突っ込めない。
せいぜい出来ることと言えば、ゴールが決まったら「ゴール決まりましたよ!」のお知らせを
呼び出し元に返すぐらい。
ボール自身は「自分がどこに転がるのか」しか興味が無く、誰が蹴ろうと狙ってるのかとか
赤軍青軍で点数にどれだけ差が開いてるのかとか知ってる必要が無い。
で、呼び出し元が変更されるわけだ。
main.cpp
void GameFlame()
{
static const int White = GetColor(255,255,255);
int lx = 0,m = 0,l=0;
switch(game.getState()){
case GAMEINIT:
DrawString(360,l,"GameInit.",White);l+=20;
for(int i=0; i<10; i++){
if(player[i].getStatus()!=WAIT)player[i].Move();
else ++m;
}
if(m>=10) game.setState(PLAY);
break;
case PLAY:
GamePlay();
break;
default:
DrawString(360,l,"DefaultError??",White);l+=20;
WaitKey();
game.setState(PLAY);
break;
}
}
そういや、Game() 関数は Gameクラス作ったから名前変えたっけ。
前までのプログラムでは static 宣言でゲームの状態を管理してたけど、いつのまにか
なぜかINIT、PLAY以外の状態になるんだ。
それが原因で、誰も動かないで時間だけが過ぎていくフリーズ状態になる。
さんざん調べたけど理由がわからない。安易な想像は出来るんだけど、どこで何が原因で
起こってるのか特定できない。
どこかで勝手に書き換わるのを防ぐためもあって、Game クラスなんて定義してみる。
Game.h
// Game.h
#pragma once
enum GameState {GAMEINIT, PLAY};
class Game
{
GameState gameState;
int RedScore;
int BlueScore;
public:
void Init();
GameState getState(){return gameState;}
void setState(GameState st){gameState = st;}
int getBlueScore(){return BlueScore;}
int getRedScore(){return RedScore;}
void addBlueScore(){BlueScore++;}
void addRedScore(){RedScore++;}
};
void Game::Init()
{
RedScore = BlueScore = 0;
gameState = GAMEINIT;
}
クラスにするほどでもないような気もするけど、クラスにしないといけないような気もする。
ゲームの状態管理をこうやって外に出しても、やっぱりフリーズする。
キャラ同士が重なって、ボールの奪い合いになると起こりやすい気がするんだけど、
そんな気がするだけで単独行動でも止まったりするからなあ。
蹴り出しの瞬間がほぼ確実に怪しいんだけど。
それはそうと話はメインのほうに戻る。
大方の予想通り、プレイヤーの移動部分とか長くなってきたので切り出す。
テスト用にへろっと書いたのをまだ使いまわしてるのが怖い。
main.cpp
void GamePlay()
{
int m = 0;
// プレイヤーを動かす
for(int i=0; i<10; i++){
if(player[i].getStatus()!=WAIT){
player[i].GoLocate(CHASE_BALL,ball.getBx(),ball.getBy());
player[i].Move();
if(Distance(player[i].getPx(),player[i].getPy(),ball.getBx(),ball.getBy())<25){
if(player[i].getTeam()==BLUE) {
if(ball.getBx()>400) ball.Kick(600,GetRand(80)+110,6.0);
else ball.Kick(600,GetRand(300),4.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);
}
} else {
++m;
// 目指せ小学生サッカー
// 自分に近いところにボールが来たら蹴りに行く
// 距離2500、50ピクセル半径
if(Distance(player[i].getPx(),player[i].getPy(),ball.getBx(),ball.getBy())<2500){
player[i].GoLocate(WAIT,ball.getBx(),ball.getBy());
player[i].Move();
if(Distance(player[i].getPx(),player[i].getPy(),ball.getBx(),ball.getBy())<25){
if(player[i].getTeam()==BLUE) {
if(ball.getBx()>400) ball.Kick(600,GetRand(80)+110,6.0);
else ball.Kick(600,GetRand(300),4.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);
}
}
}
}
// もし誰も動いてなければ、誰かひとりランダムで決めてボールに走らせる
if(m>=10){
player[GetRand(10)].GoLocate(CHASE_BALL,ball.getBx(),ball.getBy());
}
if(ball.Move()){
// ゴール!!
if(ball.getBx()<300)game.addRedScore();
else game.addBlueScore();
// ボールを中央に戻し、プレイヤーも元のポジションに帰らせる
for(int i=0; i<10; i++)player[i].GoHomePos();
ball.Init(300,150);
game.setState(GAMEINIT);
}
}
ほんと、ここまで大改造してしまったらどこがどうなったのか自分でもわからんな。
一番最後のブロックは、ボール移動関数がtrueのとき。
ゴール決まったんでどちらのゴールに入ったかを呼び出し元で確認し、点数を加える。
ゲームステートをINIT状態にして、ボールを中央に戻し、プレイヤーを帰らせる。徒歩で。
あと、上ブロックの上半分は大元とほとんど変わらない。BALL_CHASE状態のプレイヤーを
ひたすらボールに追いつくまで走らせるだけ。
ここの下半分は、ボールが自分の近くにいたらちょっと追いかけてみようかなと。
やってることは上半分のコピペでほとんど変わらないから、単純に長く見える。実際そうなんだけど。
こんなところかな。
動かしてみると案外それっぽい。
みんな必死でボールに集まるあたり、まさに小学生サッカー。
PR