Cvičenie 5
BREAKING CHANGES: PREMENOVANÉ ROZHRANIA - PRIDANÁ PREDPONA I
(ICommand, IWorld, IPhysics
) aby boli dodržané C# naming conventions
5.1 There are no ghosts here
Pri implementácií Move
ste zatiaľ neriešili kolíziu s prostredím. Napriek tomu, že už od nepamäti poznáme spôsob, ako prechádzať cez steny (nazvali ho dvere), v hre by bolo implementovať funkcionalitu, ako postavičke zabrániť v pohybe cez stenu.
Každý actor v sebe udržuje referenciu na svet, v ktorom existuje. Viete sa k nej dostať cez GetWorld()
. Svet má v sebe uložených všetkých actorov a takiež pozná rozloženie mapy - máte dostupnú metódu bool IntersectsWithWall(IActor)
. Použite ju a doplňte implementáciu tak, aby sa pomocou Move nedalo chodiť cez stenu.
5.2 Newton's legacy
Píše sa rok 1687 a sir Isaac Newton práve publikoval dielo, ktoré zmenilo svet, s názvom Principia. Zažite tento slávny moment a objavte gravitáciu:
Vytvorte si triedu Gravity
v priečinku Commands
. Nech implementuje rozhranie IPhysics
- jedná sa o okrášlený ICommand
- je tu navyše metóda SetWorld
- táto sa volá automaticky v rámci frameworku pri nastavení fyziky sveta a dostanete v nej referenciu na IWorld
- uložte si ju, budete ju potrebovať.
IPhysics.Execute()
sa automaticky volá stále v hernej slučke - viete teda v Gravity
implementovať postupné padanie predmetov:
- Každý actor implementuje 2 metódy -
bool IsPhysicsEnabled()
avoid SetPhysics(bool isPhysicsEnabled)
- pomocou nich dokážeme regulovať, či bude daný actor ovplyvnený fyzikou alebo nie - môžeme takto nechať nejaký predmet lietať vo vzduchu. -
IWorld
má v sebe metóduGetActors()
- táto vráti zoznam všetkých actorov vo svete. - Zatiaľ využite
Move
na pád, ale musíte stále vytvárať nové inštancie (dočasne zakomentujte typovú kontrolu preIMovable
vMove
(a upravte ho, nech akceptujeIActor
nieIMovable
) - za chvíľu sa k tomu vrátime a ukážeme si niečo viac o práci s typovými parametrami a Action design pattern) - nevýhoda je zvytočné vytváranie množstva objektov. - v cykle, napríklad
foreach
si implementujte pád, nezabudnite zohľadniť či je daný predmet ovplyvnený gravitáciou.
Action<T>
5.3 Aaaand V priečinku commands si vytvorte Interface IAction
:
public interface IAction<T>
{
public void Execute(T t);
}
ako už deklarácia napovedá, jedná sa o niečo podobné ako ICommand
ale je tu jeden rozdiel - v rámci execute vieme poslať nejaký parameter. Toto vieme využiť pri našej implementácií pádu - nepotrebujeme vytvárať nový objekt pre každého actora ale použijeme stále ten istý.
Pridajte si do Commands triedu Fall
, nech implementuje IAction
- máme tu dva spôsoby implementácie (použite druhý):
public class Fall : IAction<IActor>
{
public void Execute(IActor t)
{
//magic with actor
}
}
public class Fall<T> : IAction<T> where T : IActor
{
public void Execute(T t)
{
//magic with something, that implements IActor
}
}
Takýmto spôsobom sa vyhneme unboxingu a samozrejme aj zbytočnému vytváraniu množstva objektov - to však neznamená, že Command je zlý, ešte sa nám bude hodiť.
Implementujte pád (nezabudnite odstrániť komentáre).
5.4 LINQ and Lambdas
Stále prechádzať zoznamy je často nutné a nie veľmi praktické. Našťastie máme funkcionalitu, ktorá nám toto vie zjednodušiť. V C# vieme aplikovať rôzne filtre na kolekcie pomocou fukcií, ktoré dostanú ako vstupný parameter filter (lambda výraz alebo referenciu na funkciu).
Najprv potrebujeme pridať zodpovedajúci namespace (using System.Linq
) - následne vieme aplikovať tzv. extension methods na rôzne kolekcie, napríklad:
List<int> values;
//add some values into the list
List<int> negativeValues = values.Where(x => x < 0).ToList(); //select values smaller than 0, the result is then converted into a list (it is IEnumerable by default)
int smallestValue = values.OrderBy(x => x).First(); //select the smalest value (order them and select the first one)
int firstPositive = values.First(x => x > 0); //select the first positive value from the list
int numberOfPositiveValues = values.Where(x => x > 0).Count(); //count the number of positive values
List<IActor> actors//init and stuff...
IActor actor = actors.Find(a => a.GetName().Equals("player")); //select single entry
Samozrejme, dostupných metód je oveľa viac, ako sme tu ukázali, môžete si ich pozrieť napr. cez dopĺňanie kódu alebo na internete.
Modifikujte foreach
tak, aby prechádzal iba actorov, na ktorých vplýva gravitácia.
tip: ak trošku predbehneme, existuje aj foreach
verzia, ktorá akceptuje lambda výraz, tu by to fungovalo nasledovne:
//void DoStuff(IActor actor);
actors.ForEach(a => DoStuff(a));
Viac o lambda výrazoch nájdete tu: Microsoft dokumentácia
5.5 Jump
Vytvorte triedu Jump, ktorá umožní hráčovi skákať, zvážte použité rozhranie.
5.6 My very own enemy
Vytvorte triedu Enemy
(rozširuje AbstractActor
).
Použite enemy.png
ako animáciu.
V Update
implementujte funkcionalitu, že ak sa hráč k nepriateľovi priblíži na vzdialenosť N pixelov, začne ho prenasledovať. Pokiaľ je obeť v nedohľadne, nech sa nepriateľ náhodne prechádza po mape.
Random _random = new Random(); //initialize pseudorandom number generator, do this only once
//some awesome code...
int x = random.Next(min,max); //get random number from the given range