V dnešním článku se zaměříme na dva herní objekty: kanón a střelu.
Seriál stále pokračuje a teprve teď začne ta správná sranda.
Třída Cannon
Jakou funkci bude vlastně v naší hře zastávat kanón? Bude to objekt, se kterým můžete střílet a otáčet jej v určitém úhlu.
Z minulého dílu máme vytvořenou třídu GameObject, která dnes poslouží jako kostra pro třídy herních objektů. Vytvoříme tedy třídu Cannon, která bude dědit z třídy GameObject. Začneme konstruktorem.
public Cannon(Texture2D loadedTexture) : base(loadedTexture)
{
position = new Vector2(120, Game1.viewportRect.Height - 80);
}
Nic těžkého, pouze změníme umístění objektu na obrazovce. Pokud jste ještě neslyšeli o viewportRect, přečtěte si znova 2. díl, který byl nedávno aktualizován. Nyní zkusíme metodu Update.
public void Update(KeyboardState keyboardState)
{
if (keyboardState.IsKeyDown(Keys.Left))
rotation -= 0.1f;
else if (keyboardState.IsKeyDown(Keys.Right))
rotation += 0.1f;
rotation = MathHelper.Clamp(rotation, -MathHelper.PiOver2, 0.0f);
}
Argumentem předáváme metodě Update objekt s aktuálním stavem klávesnice.
Třída KeyboardState obsahuje dvě důležité metody, a to: IsKeyDown a IsKeyUp. Jako argument zde slouží hodnoty enumerátoru Keys reprezentující klávesu. Metody vrací true nebo false, podle toho, jestli klávesa je, resp. není stlačena.
V závislosti na stisknutí klávesy levé či pravé šipky se natočí kanón o jednu desetinu radiánu.
Na závěr zajistíme, aby se kanón mohl otáčet pouze ve zvoleném úhlu. Na to použijeme metodu Clamp (v překladu svěrák, docela to sedne) třídy MathHelper. První argument je proměnná s rotací, kterou chceme zkontrolovat, druhý je minimální rotace a třetí je maximální rotace.
Jako minimální rotaci jsem zvolil -PiOver2 (pí lomeno dvěma). Proč? Ve hrách se úhly označují radiány a ne stupni. Celý úhel, tedy 360°, je 2π radiánů. Přímý úhel je π radiánů a pravý úhel odpovídá π/2 radiánům. PiOver2 je tedy 90°. Samozřejmě se dají stupně převádět na radiány, ale tady to není potřeba, protože to jsou „kulatá čísla“.
Máme funkční třídu kanónu, teď ho ještě použít ve hře. Vytvořte jeho instanci v třídě Game1.
private Cannon cannon;
A v metodě LoadContent ji inicializujeme.
cannon = new Cannon(Content.Load<Texture2D>("Sprites\\cannon"));
Do metody Update přidáme volání metody Update kanónu.
cannon.Update(keyboardState);
A v metodě Draw jej vykreslíme.
cannon.Draw(spriteBatch);
Volání metody Draw umístěte mezi volání spriteBatch.Begin() a spriteBatch.End()
A to je vše, nyní se vám při spuštění hry vykresluje kanón, se kterým můžete otáčet.
Třída CannonBall
Z kanónu budeme moci samozřejmě střílet. Aby bylo co střílet, musíme vytvořit nějaký projektil. Střela se bude po vypálení pohybovat ve směru rotace kanónu v momentu vystřelení.
Naše třída bude opět dědit z třídy GameObject, doplníme jen pár členů.
Jakmile se střela dostane mimo herní prostor nebo bude zničena, už ji nepotřebujeme. O tom, že ji nepotřebujeme, budeme informovat prostřednictvím veřejné vlastnosti typu boolean alive.
public bool alive{ get; private set; }
Použil jsem auto-vlastnost, která je však až v C# 3.0. Pokud používáte Visual Studio 2005, musíte použít starý způsob.
Druhým členem bude rychlost (a zároveň směr). Vytvoříme soukromý Vector2 s názvem velocity.
private Vector2 velocity;
Následuje konstruktor. Klasika, stačí jen definovat nové proměnné, zbytek přenecháme konstruktoru z třídy GameObject.
public CannonBall(Texture2D loadedTexture) : base(loadedTexture)
{
alive = false;
velocity = Vector2.Zero;
}
Další na řadě je metoda Update(). Zde zkontrolujeme, zda je střela v herním prostoru a pokud ano, tak zařídíme, aby se posunula ve svém směru.
public void Update()
{
if (!Game1.viewportRect.Contains(new Point((int)position.X, (int)position.Y)))
alive = false;
else
position += velocity;
}
A nakonec metoda Fire. Jak asi tušíte, metoda Fire se zavolá při „výstřelu“. Zde nastavíme proměnnou alive na true, pozici nastavíme na pozici kanónu a proměnnou velocity nastavíme na rychlost.
public void Fire(Vector2 position, Vector2 velocity)
{
this.alive = true;
this.position = position;
this.velocity = velocity;
}
Co se týče třídy CannonBall, je to zatím vše. Teď stačí implementovat střelbu do třídy Cannon a bude hotovo.
Implementace střelby
Střílet se bude při stisku mezerníku. Maximální počet vystřelených střel v jednom okamžiku nastavíme na 3.
Nejprve vytvoříme nové proměnné. Tou první je pole instancí třídy CannonBalls, tou druhou je konstanta obsahující maximální počet a ve třetí je uložen předchozí stav klávesnice.
private CannonBall[] cannonBalls;
private const int maxCannonBalls = 3;
private KeyboardState previousKeyboardState;
Do konstruktoru přidáme následující kód. Nejdříve tedy inicializujeme pole a pak všechny jeho prvky. A mezi argumenty konstruktoru přidáme Texture2D ballTexture. Samozřejmě musíte upravit volání konstruktoru v třídě Game1 – nyní bude volání v metodě LoadContent vypadat takhle:
cannon = new Cannon(Content.Load<Texture2D>("Sprites\\cannon"),
Content.Load<Texture2D>("Sprites\\cannonball"));
Nyní musíme v konstruktoru třídy Cannon zařídit inicializaci pole střel a jeho jednotlivých prvků. To uděláme takto:
cannonBalls = new CannonBall[maxCannonBalls];
for (int i = 0; i != maxCannonBalls; i++)
cannonBalls[i] = new CannonBall(ballTexture);
Následuje úprava metody Update. Tady obsloužíme stisk mezerníku a zajistíme aktualizaci střel.
else if (keyboardState.IsKeyDown(Keys.Space) && previousKeyboardState.IsKeyUp(Keys.Space))
foreach (CannonBall ball in cannonBalls)
if (!ball.alive)
{
ball.Fire(position, new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) *5.0f);
break;
}
Zjistíme, zda je stisknut mezerník a zda nebyl již předtím stlačený. Tím zajistíme, že se střela vypustí jen v případě čistého stisknutí klávesy mezerník - tedy že předtím již klávesa nebyla stlačená. Pak projdeme všechny střely, a jakmile narazíme na nějakou nepotřebnou, tak ji „resuscitujeme“ a odpálíme.
Při odpálení předáme střele směr tím, že rotaci „zkonvertujeme“ na vektor. Když bude rotace kanónu 0, cosinus rotace bude 1 a sinus rotace bude 0. Když to vložíme do vektoru jako směr, střela poletí rovně doprava. Pokud nevíte co je sinus a cosinus, tak se podívejte na Googlu po goniometrických funkcích.
V metodě Update ještě aktualizujeme všechny letící střely.
foreach(CannonBall ball in cannonBalls)
if(ball.alive)
ball.Update();
A nakonec uložíme aktuální stav klávesnice pro příští volání metody Update.
previousKeyboardState = keyboardState;
Samozřejmě musíme letící střely nějak vykreslit. To zařídíme v metodě Draw.
foreach (CannonBall ball in cannonBalls)
if(ball.alive)
ball.Draw(spriteBatch);
Je to skoro to samé jako v metodě Update, ale tady voláme metodu Draw.
Závěr
Tak, to je pro dnešek vše. Získali jsme funkční kanón, který se otáčí a střílí. Ovšem střílet jen tak do prázdna je nuda, proto se příště podíváme na nepřátele.