Sveiki! Ir jauna diena un "Lidinators" turpina savu lidojumu. Domāju, ka vakardienas rezultāts nav pārāk iespaidīgs. Jā, varam ar dažām koda rindiņām parādīt kuģīti uz ekrāna, bet ko tas dod? Neko daudz, tikai sākumu jaunai spēlei. Turpinot tādiem pašiem maziem solīšiem, šodien pievienosim iespējas pārvietot kuģi.
Lai varētu ko pārvietot, nepieciešams zināt tā atrašanās vietu. Tāpēc papildināsim kodu ar mainīgajiem, kuros glabāt kuģa koordinātes un ātrumus. Ātrumi ir daudzskaitlī, tādēļ, ka vēlos atseviški izdalīt pārvietošanās ātrumu uz priekšu un rotācijas ātrumu. Līdz ar to pašreizējās mainīgo definīcijas būs šādas:
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D shipSprite;
Vector2 shipLocation;
float speed, rotationSpeed;
Tāpat derētu nodefinēt konstantes ātruma ierobežojumiem, lai kuģis neieskrietos pārāk ātri un neizsistu caurumu monitora sānos.
const float MaxSpeed = 10;
const float MaxRotationSpeed = 3;
Spēles inicializācijas metodē jānovieto kuģi koordinātu sākumpunktā:
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
shipLocation = new Vector2(0, 0);
base.Initialize();
}
Un jāpārveido zīmēšanas metodi, lai tā zīmētu kuģi, ņemot vērā tā atrašanās vietu.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(
shipSprite,
shipLocation,
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Palaižot spēli var redzēt, kas ir sanācis.
Domāju, ka nevienam nav pārsteigums, ka datoriem koordinātu sistēma parasti atšķiras no tās, ko mācīja skolā. T.i. koordinātu sākumpunkts ir ekrāna labajā augšējā stūrī, nevis ekrāna centrā. Līdz ar to ir nepieciešams kompensēt divas lietas:
- Atšķirīgu koordinātu sākumpunktu
- To, ka gariņu zīmē no stūra, nevis no centra
Lai neitralizētu pirmo punktu, pietiek izrēķināt sākuma koordinātes pēc ekrāna izmēriem. Un arī otru problēmu atrisina līdzīgi - atņemot no zīmēšanas koordinātēm kuģa centra koordinātes. Izmainītā zīmēšanas metode:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
Vector2 center = new Vector2(
Window.ClientBounds.Width / 2,
Window.ClientBounds.Height / 2);
Vector2 shipCenter = new Vector2(shipSprite.Width / 2, shipSprite.Height / 2);
spriteBatch.Begin();
spriteBatch.Draw(
shipSprite,
center + shipLocation - shipCenter,
Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Un ekrānšāviņš, lai pārliecinātos, ka tas darbojas
Sagatavošanās darbi ir pabeigti, jāsāk kustināt kuģi. Spēles stāvokļa pārrēķināšanai un lietotāja ievades saņemšanai ir paredzēta spēles Update() metode, kuru tad arī ir jāpapildina ar nepieciešamo loģiku. "Lidinatora" gadījumā algoritms ir vienkāršs:
- Ja ir nospiests taustiņš "uz augšu", tad palielina kuģa ātrumu par 3, bet nepārsniedzot maksimālo ātrumu;
- Ja ir nospiests taustiņs "uz leju", tad samazina kuģa ātrumu par 3, bet kuģa ātrums nedrīkst būt negatīvs;
- Ja ir nospiests taustiņš "pa kreisi", tad palielina kuģa rotācijas ātrumu par 2, bet tas nedrīkst pārsniegt maksimālo rotācijas ātrumu;
- Ja ir nospiests taustiņš "pa labi", tad samazina kuģa rotācijas ātrumu par 2, bet tas nedrīkst būt mazāks par negatīvu maksimālo rotācijas ātrumu;
Lai uzzinātu par nospiestajiem taustiņiem, nepieciešams izmantot Keyboard klases metodi GetState(), kas atgriež KeyboardState struktūru. Savukārt KeyboardState satur metodi IsKeyDown(), ar kuras palīdzību var pārliecināties vai konkrēts taustiņš ir nospiests. Iepriekš aprakstītajam algoritmam atbilst šāds kods:
KeyboardState currentState = Keyboard.GetState();
if (currentState.IsKeyDown(Keys.Up))
{
speed += 3;
}
if (currentState.IsKeyDown(Keys.Down))
{
speed -= 3;
}
if (currentState.IsKeyDown(Keys.Left))
{
rotationSpeed += 2;
}
if (currentState.IsKeyDown(Keys.Right))
{
rotationSpeed -= 2;
}
if (speed < 0)
{
speed = 0;
}
if (speed > MaxSpeed)
{
speed = MaxSpeed;
}
if (rotationSpeed > MaxRotationSpeed)
{
rotationSpeed = MaxRotationSpeed;
}
if (rotationSpeed < -MaxRotationSpeed)
{
rotationSpeed = MaxRotationSpeed;
}
Protams, ar ātruma rēķināšanu nepietiks, lai kuģis tiešām pārvietotos. Šajā pašā metodē ir nepieciešams uzrakstīt arī stāvokļa izmaiņu loģiku. Šajā brīdī es sapratu, ka kodā pietrūkst kuģa šābrīža leņķis un tā attēlošana. Ja pirmo trūkumu var atrisināt ar jauna mainīgā ieviešanu, tad ar otro būs nedaudz sarežģītāk, jo nāksies izmantot citu zīmēšanas metodi, kurai ir stipri vairāk parametri. Ja pirmā problēma (trūkstošais leņķis) ir atrisināta, pārvietošanās loģiku var uzrakstīt salīdzinoši vienkārši, ja neskaita, ka nepieciešams atcerēties ģeometrijas kursu. Sākumā izmainam kuģa leņķi atbilstoši rotācijas ātrumam, savukārt pēc tam izmainam kuģa koordinātes atbilstoši tā ātrumam un leņķim. Pieņemot, ka kuģa leņķis tiek glabāts grādos, tas izskatītos šādi:
shipAngle = shipAngle - rotationSpeed;
shipLocation.X += speed * (float)Math.Sin(shipAngle / 180 * Math.PI);
shipLocation.Y -= speed * (float)Math.Cos(shipAngle / 180 * Math.PI);
Lai kuģis neizlidotu tālēs zilajās (vārda tiešā nozīmē), vajadzētu arī ātrumu samazināt, tāpēc metodes beigās ātrumu samazina par vienu vienību, bet rotācijas ātrumu - uz pusi:
speed--;
rotationSpeed /= 2;
Kad kuģis, vismaz teorētiski, ir spējīgs pārvietoties, nepieciešams izmainīt zīmēšanas kodu, lai arī leņķis būtu redzams:
spriteBatch.Draw(
shipSprite, //1
center + shipLocation - shipCenter, //2
null, //3
Color.White, //4
shipAngle * (float)Math.PI / 180, //5
shipCenter, //6
1, //7
SpriteEffects.None, //8
0 //9
);
Kā redzams, klāt ir nākuši vairāki parametri, tāpēc paskaidrošu, ko nozīmē katrs no parametriem:
- gariņš kuru zīmēt;
- koordinātes, kurās zīmēt gariņu;
- taisnstūra struktūra, kas nosaka, kuru daļu no gariņa zīmēt (ja norādīta null vērtībā, tad zīmē visu)
- tonis, kas tiek lietots zīmēšanai (baltā krāsa nozīmē - zīmēt netonētu)
- rotācijas leņķis
- rotācijas centrs
- mērogmaiņas koeficients
- efekts (nekāds, apvēršana horizontāli, apvēršana vertikāli)
- z koordināte
Domāju, ka šeit nav nekāda raķešzinātne (hmm, varbūt tomēr ir?), tāpēc vajadzētu būt skaidram. Neskaidros jautājumus droši varat uzdot komentāros, centīšos atbildēt.
Viens jautājums, kurš droši vien parādīsies, ir - kāpēc nepieciešami tik sarežģīti ātruma aprēķini, ja kuģis vienalga kustās samērā saraustīti? Patiesībā kuģim vajadzētu kustēties ar zināmu inerci, bet tas tā nenotiek tādēļ, ka Update metode notiek parāk bieži. Lai padarītu kuģa ātrumu atkarīgu no laika, izmantosim Update() metodes parametru gameTime, kuram ir tāda paša nosaukuma tips GameTime. Izmantojot dažādus šī tipa parametrus var iegūt informāciju par spēles stāvokli laikā. Nedaudz pārveidojot ātrumu izmaiņas tā, lai tās būtu atkarīgas no laika, nevis atjaunošanas biežuma, "spēle" paliek interesantāka.
var elapsed = (float)gameTime.ElapsedGameTime.Ticks / 1000000;
if (elapsed > 0)
{
speed -= elapsed;
rotationSpeed -= (rotationSpeed / 4) * elapsed;
}
Tas nu pagaidām būs viss. Gaidu ierosinājumus nākošajai sērijai.
Tā kā šobrīd kods jau ir nedaudz izaudzis, pievienoju pirmkodu.