SLGBaseで戦術級シミュレーションゲームを作ってみる
8.チーム処理
最終更新:
slgbase
-
view
前節までの内容は、SLGBaseの大まかな基本機能のうちの一つであるマップ、セル、ユニット移動に関する内容でした。 ここからは、もう一つの基本機能である戦術アルゴリズムについて説明していきます。 ここでいう戦術アルゴリズムは、CPUがマップ状況からユニットをどう動かすかというアルゴリズムを指します。
8-1.チームに関する情報
戦術を考える上で、チーム識別は必須です。どのユニットを動かすべきか、どのユニットを攻撃対象にすべきかをCPUが判断できなければいけません。
そして、チーム間の関係を記述できる必要があります。
チームが二つしかなく、そのお互いが敵であれば状況はシンプルです。
そして、チーム間の関係を記述できる必要があります。
チームが二つしかなく、そのお互いが敵であれば状況はシンプルです。
しかしより複雑な状況として、
- 敵同士であるA国とB国があり、B国に援軍としてC国が参戦したが、A国としては政策上C国に手を出せない
- マップ上に敵味方の他に戦闘に無関係の「市民」が存在する
などの状況を表現したい場合があります。
このような状況を柔軟に表現するため、SLGBaseのチームクラスであるTeamBaseクラスには、あるチームが敵かどうかを判定するメンバと、味方かどうかを判定するメンバの二つが用意されています。
よって、二つのチーム間の関係は、
よって、二つのチーム間の関係は、
- 敵同士(お互いに攻撃可能)
- 味方同士(お互いに攻撃不可)
- 一方的(一方は他方を攻撃できるが、その逆は不可)
のいずれかとなります。
8-2.チームクラスの実装
チームを表現するクラスは、SLGBase内のTeamBaseクラスを継承することで作成できますが、SLGBaseではあらかじめ敵同士の関係を表現したサンプルクラス(TeamSample1,TeamSample2)が用意されています。
実装例として下に示します。
実装例として下に示します。
UnitBase.cs(一部:このコードはSLGBase内に実装済み)
public class TeamSample1 : TeamBase
{
public override bool IsFriendry(TeamBase team)
{
return (team is TeamSample1);
}
public override bool IsEnemy(TeamBase team)
{
return (!IsFriendry(team));
}
}
public class TeamSample2 : TeamBase
{
public override bool IsFriendry(TeamBase team)
{
return (team is TeamSample2);
}
public override bool IsEnemy(TeamBase team)
{
return (!IsFriendry(team));
}
}
8-3.サンプルデータに対するチーム情報の付加
サンプルのチームクラスを使用して、二つのチームからなるユニットをマップ上に配置します。
また、その情報を画面上に表示します。
また、その情報を画面上に表示します。
Map.cs(一部)
public class Map: MapBase
{
public void Init(string filename)
{
using (StreamReader r = new StreamReader(filename, Encoding.Default))
{
string line;
string[] item;
Unit u;
int row = 0;
while ((line = r.ReadLine()) != null)
{
item = line.Split("=,".ToCharArray());
if (item.Length > 0)
{
switch (item[0].ToUpper())
{
case "SIZE":
_cols = int.Parse(item[1]);
_rows = int.Parse(item[2]);
_cells = new Cell[_cols,_rows];
break;
case "MAP":
for (int i = 1; i < item.Length; i++)
{
_cells[i - 1, row] = new Cell((CellEnum)int.Parse(item[i]))
{ X = i - 1, Y = row };
}
row++;
break;
case "UNIT":
u = new Unit((UnitEnum)int.Parse(item[1]));
u.SetPos(int.Parse(item[2]), int.Parse(item[3]));
//add 8-3
switch (item[4].ToUpper())
{
case "TEAM1":
u.Team = new TeamSample1(); break;
case "TEAM2":
u.Team = new TeamSample2(); break;
default:
u.Team = new TeamSample1(); break;
}
this.Units.Add(u);
break;
}
}
}
}
ViewCenter = new Position();
MoveRangeCells = new CellList();
}
// - 略 -
public void Draw(PictureBox pic, Graphics g)
{
g.Clear(Color.White);
//セルの表示サイズを基準に表示範囲を計算
int h = pic.Height / Cell.Height; //縦方向表示可能セル数
int w = pic.Width / Cell.Width; //横方向表示可能セル数
for (int x = ViewCenter.X - w / 2 - 1; x <= ViewCenter.X + w / 2 + 1; x++)
for (int y = ViewCenter.Y - h / 2 - 1; y <= ViewCenter.Y + h / 2 + 1; y++)
{
if (x >= 0 && x < Cols && y >= 0 && y < Rows)
{
Position p = Cell2View(pic, GetCell(x, y));
g.DrawRectangle(Pens.Green, p.X, p.Y, Cell.Width, Cell.Height);
//add 6-2
g.DrawString((GetCell(x, y) as Cell).Name, _font, Brushes.Green, p.X, p.Y);
}
}
//add 6-2
foreach (Unit item in Units)
{
Position p = Cell2View(pic, item);
//change 8-3
//g.DrawString(item.Name, _font, Brushes.Blue, p.X, p.Y + 16);
if(item.Team is TeamSample1)
g.DrawString(item.Name, _font, Brushes.Blue, p.X, p.Y + 16);
else
g.DrawString(item.Name, _font, Brushes.Red, p.X, p.Y + 16);
}
//add 6-2
if (MoveRangeCells != null && MoveRangeCells.Count > 0)
{
using(Brush b = new SolidBrush(Color.FromArgb(64, Color.Cyan)))
{
foreach (Cell item in MoveRangeCells)
{
Position p = Cell2View(pic, item);
g.FillRectangle(b, p.X, p.Y, Cell.Width, Cell.Height);
}
}
}
}
}
8-4.実行結果とここまでのプロジェクト
#ref error :ご指定のファイルが見つかりません。ファイル名を確認して、再度指定してください。 (画面.jpg)